我寫了一個小程式來探索 C 中的越界讀取漏洞,以便更好地理解它們;該程式故意存在錯誤并存在漏洞:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PAGE_SIZE 5
void print_welcome(void);
unsigned int get_pages(void);
char* string(char *str);
int main(void)
{
char stack_book[] = "This is the stack book.\n";
char *heap_book = malloc(50);
if(!heap_book)
{
fprintf(stderr, "malloc failed so we have to exit.\n");
return EXIT_FAILURE;
}
char *secret = string("secretinfo");
if(!secret)
{
fprintf(stderr, "Failed to allocate memory for secret\n");
return EXIT_FAILURE;
}
char answer[5] = { 0 };
unsigned int num_pages = 0;
strcpy(heap_book, "These are the contents of the heap book!\n");
print_welcome();
printf("First, do you prefer the stack or heap book (stack/heap)?\n");
fscanf(stdin, "%5s", answer);
printf("Now, how many pages do you want to print?\n");
fscanf(stdin, "%u", &num_pages);
if(strstr(answer, "heap") != NULL)
{
printf("Good choice! The heap book is a fantastic read. Printing...:\n\n");
size_t i;
for( i = 0; i < num_pages*PAGE_SIZE; i)
{
putchar(heap_book[i]);
}
}
else if(strstr(answer, "stack") != NULL)
{
printf("Excelente! The stack book is a splendid read. Printing...:\n\n");
size_t i;
for( i = 0; i < num_pages*PAGE_SIZE; i)
{
putchar(stack_book[i]);
}
}
else
{
printf("Come back when you're ready to read, childrens....\n");
}
putchar('\n');
free(heap_book);
free(secret);
return EXIT_SUCCESS;
}
void print_welcome(void)
{
printf("Welcome to speedreader 1.0!\nWe've loaded the books into memory, read at your own speed!\n");
}
char* string(char *str)
{
if(!str)
{
fprintf(stderr,"Runtime error: Bad pointer provided to string create function.\n");
return NULL;
}
size_t len = 0;
char *ret_ptr = NULL;
len = strlen(str);
ret_ptr = calloc(len 1, 1); //Account for null terminator
if(!ret_ptr)
{
fprintf(stderr, "Runtime error(string): calloc failed.\n");
return NULL;
}
strcpy(ret_ptr, str);
return ret_ptr;
}
當使用 input: 運行時heap\n100\n,我得到輸出:
Welcome to speedreader 1.0!
We've loaded the books into memory, read at your own speed!
First, do you prefer the stack or heap book (stack/heap)?
heap
Now, how many pages do you want to print?
100
Good choice! The heap book is a fantastic read. Printing...:
These are the contents of the heap book!
!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!
我的問題是:為什么這個程式只在堆上出現一次!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!secretinfo!時列印secretinfo!:
(gdb)
0x4052a0: "These are the contents of the heap book!\n"
0x4052ca: ""
0x4052cb: ""
0x4052cc: ""
0x4052cd: ""
0x4052ce: ""
0x4052cf: ""
0x4052d0: ""
0x4052d1: ""
0x4052d2: ""
0x4052d3: ""
0x4052d4: ""
0x4052d5: ""
0x4052d6: ""
0x4052d7: ""
0x4052d8: "!"
0x4052da: ""
0x4052db: ""
0x4052dc: ""
0x4052dd: ""
0x4052de: ""
0x4052df: ""
0x4052e0: "secretinfo"
0x4052f1: ""
0x4052f2: ""
0x4052f3: ""
0x4052f4: ""
0x4052f5: ""
0x4052f6: ""
0x4052f7: ""
0x4052f8: "\021\004"
0x4052fb: ""
我不明白為什么它重復了secretinfo!這么多次。
CPU 資訊(來自 1 個內核):
vendor_id : AuthenticAMD
cpu family : 23
model : 113
model name : AMD Ryzen 9 3900X 12-Core Processor
stepping : 0
microcode : 0x8701013
cpu MHz : 3792.869
cache size : 512 KB
physical id : 14
siblings : 1
core id : 0
cpu cores : 1
apicid : 14
initial apicid : 14
fpu : yes
fpu_exception : yes
cpuid level : 16
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl tsc_reliable nonstop_tsc cpuid extd_apicid pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ssbd ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xsaves clzero arat overflow_recov succor
bogomips : 7585.73
TLB size : 3072 4K pages
clflush size : 64
cache_alignment : 64
address sizes : 43 bits physical, 48 bits virtual
作業系統:Fedora 31
內核:Linux 5.8.18-100.fc31.x86_64
編譯器:gcc (GCC) 9.3.1 20200408 (Red Hat 9.3.1-2)
聯結器:GNU ld 版本 2.32-33.fc31
glibc:2.30-13.fc31
編譯器命令:gcc ./oob_read.c -o ./oob_read -g3
如有必要,可以提供其他資訊。
uj5u.com熱心網友回復:
由于stdout是行緩沖,putchar不直接寫入終端;它將字符放入緩沖區,當遇到換行符時會重繪 緩沖區。并且緩沖區stdout恰好位于您heap_book分配后的堆上。
所以在你的副本中的某個時刻,你putchar擁有你的secretinfo方法的所有特征。它們現在位于輸出緩沖區中。稍后,heap_book[i]位于stdout緩沖區本身內,因此您會遇到secretinfo那里的副本。當您putchar這樣做時,您可以有效地在緩沖區中更遠的地方創建另一個副本,并重復該程序。
您可以在除錯器中驗證這一點。標準輸出緩沖區的地址,在 glibc 上,可以通過p stdout->_IO_buf_base. 在我的測驗中,它正好過去了 160 個位元組heap_book。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/400298.html
