DVWA(Damn Vulnerable Web Application)
DVWA(Damn Vulnerable Web Application)是一個用來進行弱點安全測試的網站系統,旨在為安全專業人員測試自己的專業技能和工具提供合法的環境,幫助web開發者更好的理解web應用安全防範的過程。
DVWA共有十個模組,分別是Brute Force(暴力破解)、Command Injection(命令注入)、CSRF(跨站請求偽造)、File Inclusion(檔案包含)、File Upload(檔案上傳)、Insecure CAPTCHA(不安全的驗證碼)、SQL Injection(SQL注入)、SQL Injection(Blind)(SQL盲注)、XSS(Reflected)(反射型跨站腳本)、XSS(Stored)(儲存型跨站腳本)。
需要注意的是,DVWA 的程式碼分為四種安全級別:Low,Medium,High,Impossible。初學者可以通過比較四種級別的程式碼,接觸到一些PHP代碼審計的內容。
Command Injection (low)
Command Injection,即命令注入,是指通過提交惡意構造的參數破壞命令語句結構,從而達到執行惡意命令的目的。命令注入攻擊是網站中常見的漏洞之一,國外著名的Web應用程式Discuz!、DedeCMS等都曾經存在過該類型漏洞。
在這一題根據頁面提示需要我們輸入 IP Address,我們可以有兩種方式測試,
- 黑箱測試
- 白箱測試
針對黑箱,就是不斷嘗試輸入各種符號、字串進行拼接造成命令注入執行,
例如 ; 分號、" 雙引號、' 單引號、‵ 反引號、&、|、?、*、${IFS}、\ 反斜線 等
- 127.0.0.1;ls
- cat /etc/pass?d
- cat /etc/pass*
- cat${IFS}/etc/passwd
- a=/etc;b=/passwd;cat $a $b
- c\a\t /etc/passwd
- cat /etc/pass""wd
- cat /etc/pass''wd
不過,本次系列主要還是針對白箱進行講解,所以直接看Source code,第3行可看到直接透過POST進行送出資料,然後第5行是要你輸入IP,接著進入到第8行開始判斷系統是Windows還是*nix,依據系統類型進入到相對應的指令(第10行或第14行),本次環境是架設在Kali Linux上,所以會直接進入到第14行。而當我們ip輸入127.0.0.1後,在後台則會執行ping -c 4 127.0.0.1。
在整段程式碼中,可以看到無進行任何字串過濾,於是我們可以直接在$target後面拼接上我們要的payload。
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
$html .= "<pre>{$cmd}</pre>";
}
?>
前面提到,當我們輸入127.0.0.1送出後,會變成ping -c 4 127.0.0.1,那麼該如何進行拼接我們想要帶入的指令呢?
這時,可以用;、&等方式進行拼接,127.0.0.1;ls或127.0.0.1&ls,會變成ping -c 4 127.0.0.1;ls及ping -c 4 127.0.0.1&ls
於是便成功進行了Command Injection的攻擊,也就是說我們可以在IP後面接上任意指令,達到惡意攻擊。
我們看一下Linux Shell,看看為何如此?
如果你仔細觀察,可以發現到為何第一個紅框是先執行Ping後執行ls,而第二個紅框則是先進行ls再執行ping。
簡單解釋下,如果是用&進行拼接,Command 1& Command 2,那麼會先執行Command 1,然後不管Command 1有沒有執行成功,都會繼續執行Command 2。
那麼如果是使用&&呢? 127.0.0.1&&ls
如果使用兩個&,Command 1 && Command 2,那麼也一樣會先執行Command 1,但會必須等Command 1執行成功後,才會執行Command 2。
我們可以觀察到,如果使用兩個&來進行拼接,Command 1執行失敗,那麼Command 2是不會繼續執行的。
這就好像程式碼中,1個=和2個==與3個 ===再進行判斷式時,判斷方式是不一樣的。
if (username = password)
if (username == password)
if (username === password)
前面介紹完了,我們在IP後面接著使用ls、id等指令進行一番搜索外,其實Command Injection也可以進行拿Webshell的動作,例如我們在後面接wget去下載一個webshell。
從伺服器上可以看到我們在網頁端成功讓該主機進行了下載webshell.php的行為,有了webshell後,接下來可以進行的事情又更多了,這邊就先不提了。
Command Injection (medium)
直接看Source code,主要是第8與第9行,可以看到添加了黑名單過濾,把&&和;都過濾掉了,其餘程式碼沒什麼改變。
既然過濾了兩個&,那麼在這邊我們可以直接使用一個&來進行拼接,達到Command Injection,也可以用 | ,進行拼接,127.0.0.1|ls。
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
$html .= "<pre>{$cmd}</pre>";
}
?>
輸入127.0.0.1&ls,即可看到成功進行了注入。
除此之外,我們還可以利用str_replace來構造我們的payload,例如127.0.0.1&;&ls,在這邊str_replace會將;過濾,變成127.0.0.1&&ls,這樣又成功bypass過濾機制,達到注入攻擊。
Command Injection (High)
直接看Source Code,第8行開始,進行更多的符號字元過濾,基本上都將符號給過濾了,但仔細看第11行,|符號的後面多了一個空格字串,這可能是開發人員的一疏失,因此我們可利用這個疏失來達成注入。
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]);
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
$html .= "<pre>{$cmd}</pre>";
}
?>
我們輸入127.0.0.1|ls,即可成功進行Command Injection。
另外,因為該黑名單是過濾| <---空白字串在|的右邊,於是我們可以改變成127.0.0.1 |ls,空白字串在|的左邊來進行繞過注入。
除此之外,甚至也可搭配反引號 ‵ 進行拼接,127.0.0.1‵|‵ls。
Command Injection (Impossible)
直接看Source Code,第5行添加了Anti-CSRF Token,第9行添加了stripslashes,而stripslashes會將過濾的字串變成反斜線,導致無法用127.0.0.1&;&ls類似此方法進行繞過。第12行explode進行了字串的切割,接著第15行將IP地址用[]陣列分開,並透過is_numeric檢測string是否為數字字串,導致無法輸入符號來拼接命令注入。
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );
// Split the IP into 4 octects
$octet = explode( ".", $target );
// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
$html .= "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
$html .= '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>