imaginaryctf2024

忘记打了。。遂写一份复现wp

readme

这里ng做了个判断,是否直接访问静态文件,直接访问的话就返回404了

ng配置无法直接通过url读取静态目录public下的文件,但是我们的flag又在其中,实际上node也将public设置为静态目录,假如说ng没有做配置的话应该是可以直接通过url访问到flag.txt的,怎么办呢。。。

这么出题就说明二者某些特性上必有冲突,要么是对url的解析上有区别,要么就是对一些不可见字符的处理上有区别,不管怎么说,fuzz吧

fuzz字典用url编码的字符从%00到%ff

在/flag.txt后面设了个点fuzz但是结果不是404就是300,

emmm,一个点不行那就两个试试

奇迹出现了!%2f%2e的时候成功返回200,(解码后是/.)到底是为什么日后再做研究(flag已立

上网看了眼其他师傅的wp。。啊都是从Dockerfile直接看的吗???那玩意居然是真的???

journal

一处很明显的assert rce,且没有任何过滤

唯一的瑕疵就是之前只知道assert可以rce但是从来没有实战过导致这道题自己本地docker里里外外调试了好多次才成功,assert里面是断言,因此不能用分号隔开多个语句必须要用&&,也是灵光一现了。。为什么不问问gpt呢,因为懒(bushi)

哦哦对了记得&&一定要url编码一下(痛苦面具

一开始我是直接在docker里替换掉assert里的语句来测试的,docker内成功了但是在web怎么也没办法,然后我把整个断言的语句打印出来,发现&&没编码的时候传到后端后面是什么也没有了,这一刻我感觉天崩地裂,一年多白学了(bushi),总之url中有特殊含义的字符('#'"&"等)想作为参数传入的话务必编码一下吧

上面payload解码后是

1
zz','..')==false && phpinfo();//

P2C

整的有点花里胡哨的界面,刷新一次还会变颜色

直接看后端代码吧

这个xec()函数主要逻辑就是把我们通过表单传入的代码嵌入到一段代码中,然后写入一个临时文件并运行,最后再删除掉文件,怎么个插入法就不细看了,直接注释掉remove看看最后生成的代码长什么样就好啦

随便写点什么进去

在docker中可以看到是在code后随机生成了一串字符串,然后我们写入的代码直接拼接到main函数中,随后调用main函数

妥妥的rce,但是没有回显,直接反弹shell

但是监听段是一片风平浪静

不知道发生了什么就去docker里看看吧,提示的有点看不太懂但是根据经验这种以shell环境(像zsh,sh,bash这种的)为报错头的,基本上都是因为当前shell的操作环境不支持当前命令中的一些操作,但是在bash环境中直接执行语句是可行的,怎么办呢,那就让它在bash环境下运行这一语句

bash -c即可

监听端也是收到了反弹的shell

crystals

最有点无语的一题,从源代码可以看出是rubby的后端,用了sinatra框架

flag写在了hostname中,复现的话题目没给.env得自己加

flag写在hostname中,按理来说应该需要rce了啊,但是这个只有静态页面,用的所有资源还几乎都是latest的,整的有点崩溃,那么多解绝不会是0day啊,天上地下地寻找各种漏洞,无果遂反

自己憋了半天没搞定,看一眼别人的wp吧,直接通过特殊字符报错直接拿到flag好家伙。。上次看到这种类型的还是ASIS某次把flag写在路径中然后通过报错得到。。服啦

自己fuzz一下试试,把这个勾选去掉

这几个都是会引起400的

有点无语的一道题

The Amazing Race

没有附件呜呜呜先放着

readme2

源码奉上,起docker的时候记住只映射一个4000端口

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
const flag = process.env.FLAG || 'ictf{this_is_a_fake_flag}'

Bun.serve({
async fetch(req) {
const url = new URL(req.url)
if (url.pathname === '/') return new Response('Hello, World!')
if (url.pathname.startsWith('/flag.txt')) return new Response(flag)
return new Response(`404 Not Found: ${url.pathname}`, { status: 404 })
},
port: 3000
})
Bun.serve({
async fetch(req) {
if (req.url.includes('flag')) return new Response('Nope', { status: 403 })
const headerContainsFlag = [...req.headers.entries()].some(([k, v]) => k.includes('flag') || v.includes('flag'))
if (headerContainsFlag) return new Response('Nope', { status: 403 })
const url = new URL(req.url)
if (url.href.includes('flag')) return new Response('Nope', { status: 403 })
return fetch(new URL(url.pathname + url.search, 'http://localhost:3000/'), {
method: req.method,
headers: req.headers,
body: req.body
})
},
port: 4000 // only this port are exposed to the public
})

这个后端分了两个服务,3000端口的服务是用来获取flag的,判断访问路径如果是/开头则返回Hello World,如果是flag.txt就返回flag;再看4000端口的服务,限制的挺苛刻的,首先就是url中不能出现'flag',其次请求头键值对中都不能有'flag',再就是将url传入URL进行实例化后的href属性中不能有'flag'

看到new URL就想起去年ACTF的一道xss,正好下面的第二个URL对象实例化中的参数部分是可控的,直接上图吧:

当第一个path参数的位置变成了http://****之后,实例化对象的host,hostname,href还有origin的值都是可控的,那么怎么跳转到localhost:3000/flag.txt的同时保证中不出现flag字样呢?

这里利用点在于它使用了一个fetch,导致可以允许重定向,因此我们只需要在自己的vps中写入一个重定向的服务跳转到http://localhost:3000/flag.txt即可

开始操作

这里在本机上再起了个docker服务用于重定向

结合上面拼接的规则,是将pathname和search拼接起来,

不妨把整个url对象打印出来,重启服务

按照上面的URL的漏洞对url进行拼接,但是出现如下非预期的结果

去docker的日志看一眼是怎么个事,pathname前面怎么多了条杠,啧

总感觉思路应该没问题,于是又去浏览器控制台鼓捣了半天,结果发现:

(也是运气值拉满了。。)

这灵感不就来了吗,不需要前面的http协议也可以!

那么再结合已有的一条杠,我们只需要自己再加一条杠即可

搞定~

Pwning en Logique

一道很简单的格式化字符串漏洞,最大的难点在于使用的语言Prolog是完全全新的,没接触过

在GPT的帮助下了解完程序的逻辑之后,寻找用户可控的参数点的位置:

在greet/1这个位置,用户传入参数是可控的,可以传入format和greeting参数,并且这两个参数随后直接传入format/2中进行格式化字符串,怀疑这个地方存在漏洞

Google搜索Prolog format,查阅官方文档

直接调用print_flag

作者

Potat0w0

发布于

2024-07-24

更新于

2024-07-26

许可协议


评论