Midnight Sun CTF 2025 WEB
Hackchan
Hackchan is your friendly neighborhood grocery store, with an exclusive loyalty program where points can be redeemed for amazing rewards. The system is said to be bulletproof, but there are whispers of a vulnerability that would allow one into the Billionaire Club by obtaining one billion loyalty points in their Hackchan account.
Condition of getting flag
在views.py中看见了得到flag的条件
Saw the condition for getting the flag in views.py
:
|
|
即当当前用户的balance大于999_999_999的时候就可以得到flag,所以要看一下逻辑。
That is, when the current user’s balance is greater than 999_999_999, the flag can be obtained. So we need to examine the logic.
bot处理流程
在bot.js中处理problem中的url的过程,这个地方就可以构造路由让admin去访问。
In bot.js, the URL in problem is handled — this is where we can construct a route to make the admin visit it.
|
|
然后和balance有关的函数只有
Then, the only function related to balance is:
|
|
然后我们现在要找到一个地方去得到balance,于是翻看源码 这里用了TF-IDF,将question转换为了数值向量
Now we need to find a place to gain balance, so let’s go through the source code. Here, TF-IDF is used to convert the question into a numeric vector.
|
|
然后我们主要看到处理fac这里。
Now focus on how faq is handled.
|
|
所以我们只需要找一个label匹配到我们的.swp文件(题目故意留的,可以嵌入xss,but我利用vim -r 看不出来是什么问题,所以等wp吧,再补上)
So we only need to find a label that matches our .swp file (intentionally left by the challenge, can be used to inject XSS — but I couldn’t see the problem clearly using vim -r, so I’ll wait for the writeup to patch this part).
.
开头的文件,所以在文件系统里面会放在最前面,再回顾我们的fac内容,这里只贴出部分
Since it’s a file starting with .
, it will be listed at the top of the filesystem. Looking back at our faq content (partial screenshot):
action=fac&question={text}
的text内容与对应的question对应上即可
We just need to match media in it, and it will render the .swp file.
So we only need to let action=faq&question={text}
— where the text matches a question — and it will work:
https://hackchan-mjk2mpay.ctf.pro/?action=faq&question=How can I get in touch with your PR department for collaboration?<script>alert('hacked by dt')</script>
That will trigger XSS. Next is straightforward. Remember the bot from earlier? We just need to control the XSS to make the bot transfer money to us.
So let the bot visit:
http://web:8000/?action=create-transaction9
然后post过去
recipient=deletee&amount=9999999999
But only 10 units can be transferred at a time. Let’s review the logic. Note that processing is split into two phases:
confirmed_transactions = Transaction.query.filter(Transaction.status == 'confirmed').all()
所以我们可以在confirm_transaction
之后的间隙将订单的status更改一次为confirmed即可完成转账,也就是race condition
最后的playload:
See here: confirmed_transactions = Transaction.query.filter(Transaction.status == 'confirmed').all()
— so we can change the status of a transaction to confirmed in the gap after confirming, to complete the transfer. That’s a race condition.
Final payload:
|
|
用的<img src=x onerror="eval(atob('playload'))"/>
注意下编码问题即可,最后再次感谢HighDex
用英文说一遍:
Use <img src=x onerror="eval(atob('playload'))"/>
, just watch out for encoding issues. Finally, again, many thanks to HighDex.
In English:
|
|
Uselesscorp
扫了一下目录
Scanned the directories:
class.phpmailer.php
的日期找到对应版本为v5.2.17
Found class.phpmailer.php
and determined its version is v5.2.17:
https://github.com/PHPMailer/PHPMailer/blob/v5.2.17/class.phpmailer.php
打CVE 2016-10033
Tried CVE-2016-10033 using this exploit:
exp: https://github.com/opsxcq/exploit-CVE-2016-10033
但是发现打不通,说明环境可能和原本的不大一样。 原来是在这里:
But it didn’t work — possibly due to environment differences.
Turns out the reason is here:
Debian systems often use Exim4 as MTA, so we checked the docs: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html
发现了如下几个好玩的东西:
- ${readfile{
}{ }}可以读取文件然后将其中的换行符替换掉 - ${readsocket{…}{…}{…}{…}{…}}向本地或远程 socket 发送请求字符串,读取响应数据,并将其插入到当前字符串中。适用于与其他服务通信,如 Redis、Python 微服务、SMTP、HTTP 接口等。
- ${run,preexpand{option}}运行一个外部命令,将其标准输出作为变量
$value
,并依据返回码决定使用哪个返回值。
Found some interesting things:
-
${readfile{
}{ }} reads file content and replaces newline characters -
${readsocket{…}{…}{…}{…}{…}} sends request to socket (local/remote), reads response, useful for communicating with services like Redis, Python microservices, etc.
-
${run,preexpand{option}} executes an external command and uses its stdout as
$value
所以我们可以构造一下sendmail的请求:
注意一下这里的email的格式,遵循RFC 3696,我们用"来闭合前面的引号然后用-Ov来让后面的语句无效,这个是HighDex师傅告诉我的,基本上都适用,我们也可以用一个极短的例如@d
So we can craft a sendmail request:
Note the email format — according to RFC 3696, we use " to escape and close the previous quote, then use -Ov to nullify the following content. This trick was shared by HighDex, and it’s widely applicable. We can also use a short one like @d
.
|
|
于是最后的exp:
Final exploit:
|
|
其他playload(官方给的):
Other payloads (from the official writeup):
|
|
不过思路也是差不多的,只是一个直接read了我是弹shell
But the idea is basically the same — just that the official one directly reads the flag, while I went for a reverse shell.
写在最后
写双语的原因是起初写完后发到midnight的discord上了,但是有师傅批判说哪有人用母语写文章的(XD),于是考虑到国内和国外的的用户,我就写成了这样子。