NewStarCTF-pwn
- NewStarCTF-pwn
- week1
- overwrite
- read() 引數
- gdb
- overwrite
- week2
- week3
- week4
- week1
week1
overwrite
首先檢視檔案,保護全開,為64位
放入IDA檢視程式碼,發現關鍵函式func()
第9-12行程式碼,首先列印提示資訊“pls input the length you want to readin: ”,然後使用__isoc99_scanf
函式接收使用者輸入的一個整數並存入變數nbytes
中。接著檢查nbytes
的值,如果nbytes
大於 48,則程式呼叫exit(0)
退出。
第13、14行,列印提示資訊“pls input want you want to say: ”,然後使用read
函式從標準輸入(檔案描述符為 0)讀取資料,將讀取的資料儲存到地址為&nbytes_4
的記憶體區域,讀取的位元組數為nbytes
轉換為無符號整數後的大小(本題需要小於48)。
我們先來學習一下read()函式,它用於從檔案描述符(通常是套接字、檔案等)讀取資料,讀取開啟檔案內容
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
-
read() 引數
-
fd
:- 是檔案描述符,可以是套接字、檔案等讀取的檔案(標準輸入為 0)
-
buf
:- 是一個指向要讀取資料的緩衝區的指標,將讀取的內容儲存的緩衝區
-
count
:- 是要讀取的位元組數,檔案的長度
-
返回值:
- 如果成功,返回讀取的位元組數(可能為 0,表示已經讀到檔案末尾)。
- 如果出錯,返回 -1,並設定
errno
表示錯誤原因。
-
第15行
if ( atoi(nptr) <= 114514 )
{
puts("bad ,your wallet is empty");
}
else
{
puts("oh you have the money to get flag");
getflag();
}
如果 nptr
中的字串被轉換為數字且大於 114514
,程式會列印 flag
,否則會輸出錢包空的資訊。其中:
函式atoi ()
int atoi(const char *str);
atoi (表示 ascii to integer)是把字串轉換成整型數的一個函式,會掃描引數 nptr字串,會跳過前面的空白字元(例如空格,tab縮排)等。如果 nptr不能轉換成 int 或者 nptr為空字串,那麼將返回 0。特別注意,該函式要求被轉換的字串是按十進位制數理解的。atoi輸入的字串對應數字存在大小限制(與int型別大小有關),若其過大可能報錯-1。
const char* str1 = "1234";
const char* str2 = "56abc";
const char* str3 = "abc123";
int num1 = atoi(str1); // 轉換為整數 1234
int num2 = atoi(str2); // 轉換為整數 56,遇到非數字字元後停止
int num3 = atoi(str3); // 轉換為整數 0,開頭沒有數字
具體函式檢視atoi()參考文件
回到本題,進行漏洞分析:
nptr
和nbytes_4
之間的偏移是 0x30。如果輸入大於 0x30 的正整數,程式將會透過exit()
退出。- 觀察到,
read()
函式中使用的是unsigned int nbytes
,但在輸入校驗時nbytes
是int
型別,進行了有符號比較。這裡產生了漏洞。
read(0, &nbytes_4, (unsigned int)nbytes);
例如,-1
的 16 進製表示是 0xffffffff
,對於有符號整數來說,這是一個負數;但在無符號整數中,它代表一個非常大的正整數。因此,利用這一點,輸入一個負數值,可以繞過前面的大小檢查,將後續輸入的資料覆蓋到 nptr
,從而完成利用。
exp:
from pwn import *
#context.terminal = ['tmux','splitw','-h']
p = process('./pwn')
#p = remote('ip', port)
p.sendlineafter(b': ', b'-1')
payload = b'a'*0x30 + b'114515'
p.sendafter(b': ', payload)
p.interactive()
觀察指令碼執行順序,其順序為先傳送整數-1,使scanf函式接收的整數nbytes的值滿足小於48的條件,保持程式的執行,同時利用下面程式碼使用無符號整型轉換的nbytes值比較大小,而無符號的-1又為最大值(0xffffffff)的漏洞,達到想要的效果(後面(unsigned int)nbytes
可以給&nbytes_4
分配nbytes個位元組空間),如果輸入一個正整數,那空間必然要小於48,就無法溢位資料到nptr[72]了。
- 問題1:為什麼指令碼構造的是0x30個字元a?
答:分析變數的地址及偏移,0x80-0x50=0x30,所以輸入0x30(48)個字元後nbytes_4就滿了,再輸入就會溢位到nptr[]了
size_t nbytes_4; // [rsp+10h] [rbp-80h] BYREF
char nptr[72]; // [rsp+40h] [rbp-50h] BYREF
- 問題2:還多了114515這個數是幹嘛的
答:眾所周知
注意,這裡用的是位元組型資料(b'114515'而不是整型p64(114515)),見上文atoi(nptr)
函式,其接收引數nptr為字串,之後會轉為整型
- 問題3:為何使用此指令碼攻擊不會觸發金絲雀棧保護
答:輸入僅影響了緩衝區和部分其他變數,而未觸及金絲雀,雖然程式碼中使用__readfsqword(0x28u)
讀取了金絲雀值並儲存在v4
中,但攻擊者構造的payload
(b'a'*0x30 + b'114515'
)在填充緩衝區並溢位時,尚未覆蓋到金絲雀所在的位置,當棧空間被耗盡時,才會棧溢位。此題的溢位全部在棧內完成,故不會觸發金絲雀棧保護。注意區分緩衝區溢位和棧溢位兩者之間的區別
gdb
首先進行檢視,保護全開,64位
放入IDA檢視主函式,發發現程式碼會不一樣,既然叫gdb,那肯定就不能只單純靜態除錯了
可以看到程式邏輯是對一串字串執行了加密操作,然後需要我們輸入加密後的內容
對於加密函式,可以發現完全看不懂(不是🥰),甚至不知道哪個是加密函式(確信😢)
所以我們選擇動調檢視加密後的內容
鍵入 gdb ./gdb
並執行,先執行程式(命令run),再用 b *$rebase()
下斷點(斷在call 加密函式處)本題call函式在0x181a處,不同環境下可能不同
其中:b *$rebase()
用於設定一個斷點
b
是break
的簡寫,設定斷點*
: 表示解引用操作。用於訪問指標指向的記憶體地址的值$rebase
() 是一個 GDB 內建函式,用於將給定的虛擬地址轉換為實際的記憶體地址
綜合起來,b *$rebase(0x181a)
的意思是:使用 $rebase(0x181a)
計算出重定位後的地址;然後,在該地址處設定一個斷點。
如果用gdb開啟直接用b *$rebase(0x181a)會報錯
pwndbg> b *$rebase(0x181a)
evaluation of this expression requires the target program to be active
//表示:要對 `$rebase(0x181a)` 這樣的表示式設定斷點,需要目標程式處於活動(正在執行)的狀態。
所以先執行。這裡執行程式時遇到一個問題,就是run以後會直接執行到讓你輸入的那個位置,如果你輸入什麼了,他就會退出,用ctrl+c退出其函式即可。之後下斷點,再run到斷點處,這都是dbg的基礎操作練習啊。
可以看到,已經執行到call加密函式的位置了
執行到加密函式處,可以發現,rdi 暫存器存的是要加密的內容,rsi 存的是加密的 key
先複製下要加密內容的地址
0x7fffffffd9a7
(不同環境可能不同,此地址為加密內容"0d000721"
所指向的地址)
然後使用 ni
指令步進
注:ni
和n
(next
)的區別在於,ni
是按機器指令來執行的。也就是說,ni
會執行下一條機器指令,而不是下一行原始碼。由於一條原始碼行可能對應多條機器指令,所以ni
的執行粒度更細。
此時字串已經完成了加密,我們使用 tel 0x7fffffffd9a7
指令檢視字串(其本身和後繼地址)的內容
pwndbg> tel 0x7fffffffd9a7
00:0000│-439 0x7fffffffd9a7 ◂— 0x4557455355431d5d
01:0008│-431 0x7fffffffd9af ◂— 0x6572636573796d00
02:0010│-429 0x7fffffffd9b7 ◂— 'tkey1234567890abcdefghijk'
03:0018│-421 0x7fffffffd9bf ◂— '567890abcdefghijk'
04:0020│-419 0x7fffffffd9c7 ◂— 'cdefghijk'
05:0028│-411 0x7fffffffd9cf ◂— 0x6b /* 'k' */
06:0030│-409 0x7fffffffd9d7 ◂— 0x7ffff7ffe2e000
07:0038│-401 0x7fffffffd9df ◂— 0x7ffff7ffe2e000
其地址內容為一串十六進位制數字,不知道是什麼,用x指令精準檢視指定地址內容
pwndbg> x 0x7fffffffd9a7
0x7fffffffd9a7: "]\035CUSEWE"
文章說b'\x5d\x1d\x43\x55\x53\x45\x57\x45'
便是加密後的內容,經過分析,“]\035CUSEWE”就是加密內容
b'\x5d\x1d\x43\x55\x53\x45\x57\x45' == b']\035CUSEWE'
True
只是要以文章的形式還需要多一步構造過程(本題並未直接顯示)所以姑且就直接使用本題所得結果編寫指令碼
exp
from pwn import *
p = process('./gdb')
elf = ELF('./gdb')
data = b']\035CUSEWE'
p.sendline(data)
p.interactive()
結果
┌──(root㉿kali)-[~/Desktop/New StarCTF/gdb]
└─# python3 act.py
[+] Starting local process './gdb': pid 513654
[*] '/root/Desktop/New StarCTF/gdb/gdb'
...
[*] Switching to interactive mode
[*] Process './gdb' stopped with exit code 0 (pid 513654)
Original: 0d000721
Input your encrypted data: Congratulations!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\...