2023校赛总结

0 前言

  • 我是什么垃圾 出题给大佬做(瑟瑟发抖
  • 大概就记录一下审WP过程中佬们的非预期和发现的题中的一些问题

1 signin

  • 这题还有非预期我是没想到的 菜狗哭泣(当时出的时候也没多想 就按磐石那个出的来着

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int __cdecl main(int argc, const char **argv, const char **envp)
    {
    __int64 v4[2]; // [rsp+0h] [rbp-10h] BYREF

    v4[1] = __readfsqword(0x28u);
    put(argc, argv, envp);
    __isoc99_scanf("%ld", v4);
    if ( v4[0] > 3 )
    exit(-1);
    if ( LODWORD(v4[0]) == B )
    b4ckdo0r();
    return 0;
    }

    只要符号位为1 低32位为0x42就行 至于高32位的数值位为多少都行

  • 然后关于如何发送的问题

    但是这里读入用的是scanf,所以必须以字符串或者bytes的形式输入数字(X1ngCHEN师傅)

    p64是不行的 但我查了查p64就是将整数转换成字节啊?(不过我好像用p64只会是地址

  • patchelf还可以这样写(X1ngCHEN师傅)

    1
    patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn

2 EZest

  • 有的师傅只写了一个SigFrame
    pop_rbp_ret写/bin/sh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     b'a'*0x10 + p64(pop_rbp_ret) + p64(bss - 0x10) + p64(0x40110e)

    .text:000000000040110E 48 C7 C0 00 00 00 00 mov rax, 0
    .text:0000000000401115 48 C7 C7 00 00 00 00 mov rdi, 0 ; fd
    .text:000000000040111C 48 8D 75 F0 lea rsi, [rbp+buf] ; buf
    .text:0000000000401120 48 C7 C2 00 02 00 00 mov rdx, 200h ; count
    .text:0000000000401127 0F 05 syscall ; LINUX - sys_read
    .text:0000000000401129 C3 retn
    .text:0000000000401129
    .text:0000000000401129 main endp ; sp-analysis failed
  • 不过本来也是板子题

3 magicode

  • 这题可能出的还可以??(小心翼翼 还好当时没有把这么丑的代码改掉😏 不然师傅们就觉得贼简单了

  • 但这题之前说过 保护那里应该可以怎么弄不显示rwx段(FSCTF那

  • Ephemeral1y师傅真的强 手打shellcode来着
    pANz0e师傅add(-10)也是可以的

  • 还有Whhxy4师傅提到了绕过canary的问题

    alloca在汇编⾥⾯的效果是add rsp,rdx

    也就是1是分配⼀个栈根据rbp的位置往下减,输⼊0就是在rbp-0x10的位置开始以此类推 ,这题有

    canary所以输⼊0正好跳过canary


    emmm 我仔细看了一下 首先是sub rsp, rdx 然后是根据rsp的位置往下减,确实和canary无关 rbp的位置是固定的

    X1ngCHEN师傅说有爆破? 啊出题和做题还是有些不同的 完全没考虑到
    试了试X1ingCHEN师傅的exp,其实也并不用爆破 ,好像最后\x10完全没用到 每一次都能getshell的

    1
    payload = b'a'*(0x70) + shellcode.ljust(0x50, b'\x00')+b'\x10'

    嗯嗯 确实 magic函数的实现为

    1
    2
    .text:0000000000001568 48 8B 45 E0                   mov     rax, [rbp+var_20]		# 这个var_20我gdb看好像就是固定为0x20 然后这个位置存放的就是shellcode的起始位置
    .text:000000000000156C FF D0 call rax
  • 好好好 alloca函数是为了对齐并且多0x10(可能是为了安全) 就是如果为alloca(0x60) 实际分配的是0x70 如果是alloca(0x64) 分配的也是0x70

  • gdb应该是能看到的会多分配0x10 alloca在汇编上的效果是sub rsp, rdx

    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
    27
    28
    # 这是执行完sub rsp, rdx以后 size输入的是0x20
    0x555555555496 <main+311> sub rsp, rdx
    ► 0x555555555499 <main+314> mov rdx, rax
    0x55555555549c <main+317> and edx, 0xfff
    0x5555555554a2 <main+323> test rdx, rdx
    0x5555555554a5 <main+326> je main+344 <main+344>

    0x5555555554a7 <main+328> and eax, 0xfff
    0x5555555554ac <main+333> sub rax, 8
    ──────────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────────────────
    00:0000│ rsp 0x7fffffffdd10 ◂— 0xa3233 /* '32\n' */ # alloca(0x20)以后rsp的位置
    01:0008│ 0x7fffffffdd18 ◂— 0x0
    02:0010│ 0x7fffffffdd20 —▸ 0x7fffffffdf18 —▸ 0x7fffffffe278 ◂— '/home/yech0/sskd/hectf2023/2magicode/magicode'zhe
    03:0018│ 0x7fffffffdd28 ◂— 0xe4c8453177346a00
    04:0020│ 0x7fffffffdd30 —▸ 0x7fffffffde00 ◂— 0x1
    05:0028│ 0x7fffffffdd38 —▸ 0x55555555543d (main+222) ◂— mov dword ptr [rbp - 0x2c], eax
    06:0030│ 0x7fffffffdd40 —▸ 0x555555558020 (stdout@GLIBC_2.2.5) —▸ 0x7ffff7e1a780 (_IO_2_1_stdout_) ◂— 0xfbad2887 # rsp原先的位置


    # 我这里gdb是如何实现多0x10的:(size输入0x20以后
    0x555555555445 <main+230> lea rdx, [rax + 8] rdx:0x28
    0x555555555449 <main+234> mov eax, 0x10 rax:0x10
    0x55555555544e <main+239> sub rax, 1 rax:0xf
    0x555555555452 <main+243> add rax, rdx rax:0x37
    0x555555555455 <main+246> mov esi, 0x10
    0x55555555545a <main+251> mov edx, 0
    0x55555555545f <main+256> div rsi rax:0x3 rd:0x7
    0x555555555462 <main+259> imul rax, rax, 0x10 rax:0x30

4 fmt

  • 好好好 没有师傅用预期方法 菜狗爆哭 师傅们都太强了

  • 我都不道会有stack_chk_fail的利用 那其实能利用多次的话就完全不用给后门了 师傅们好像也没用(555555 有一个(蔚蓝师傅用了fini_array pANz0e师傅用了fini_array没有泄露libc 又改stack_chk_fail泄露然后利用后门

  • 读了题目都知道是格式化字符串漏洞了,但是正常情况下只能使用一次,这显然是无法让我们拿到shell的。

    刚开始往格式化字符串泄露canary然后构造ROP的办法来想的,但是意识到第一步获得了canary又能干什么呢?啥都干不了,就exit了。所以我们不能这么平庸地看问题。

    考虑到绕过Canary,不如与其正面硬刚,直接让Canary失效。

    众所周知,如果程序检测到Canary被更改了,那么就会在函数结束的时候调用stk_chk_fail这个函数,然后就退出了。但是我们正是可以利用这一点,用格式化字符串更改stk_chk_fail的got表地址为main函数地址,那么就可以构造较长的payload来修改Canary从而可以无限次调用main函数,也是可以通过较短的payload不修改Canary来达到不接着调用

    函数直接ret的效果。

    既然可以控制main函数的执行了,那么我们也就可以控制格式化字符串漏洞的利用了。

    于是我们这样利用漏洞:

    1.第一次格式化字符串漏洞:修改__stk_chk_fail函数的got表地址为main函数地址。

    2.第二次格式化字符串漏洞:获取栈上面__libc_start_main+128的地址。

    3.第三次格式化字符串漏洞:构建并且输入ROP,但是这里payload的长度肯定会覆盖Canary,因此我们这里一定会进入下一层main函数,但是在下一层main函数我们随便传一个短短的字符串就行了,就可以不修改Canary从而达到退出执行上一层main函数输入的ROP的效果。

    这里输入用的是scanf,遇到0d会截断,亲测system似乎打不通(我只试了本地,远程不知道),反正都有libc基址了,啥gedgets就都有了,而且ROP空间充足那么我们倒不如直接调用成功率更大的execve(X1ngCHEN师傅

    然后这里还有个问题就是高版本rop的话没有pop_rdi_ret的这种gadget 于是师傅们用了libc里的
    上面还说到system打不通 有的师傅用了execve 有的用的onegaget

  • magicode和这题enllus1on师傅都拿了一血 magicode不用多说 应该是做过fcalc那个题 这个题写的exp看不懂(最强双非22级是吧 瑟瑟发抖

  • sublime text ctrl shif L多行编辑

5 风水小狮

  • emmmm 这个应该没啥注意的 都是预期解

6 风水中狮

  • 又是enllus1on师傅 唯一解 真的强 %50$p和 %51%p都是flag的地址
  • 沃趣 好像给的解释错了 给师傅们磕一个

7 风水大狮

  • 好家伙 田总说加载沙箱的代码忘写了 非预期了 没有绕沙箱也能做的 好家伙 除了Ephemeral1y师傅用了看不懂的链子
    怪我怪我 没审好题
  • 如果有沙箱的话 就得绕 但还没看懂

8 easyweb

  • 就X1ngCHEN师傅的唯一解呢 预期解 看看就欧克

2023校赛总结
https://yech0.github.io/2023/11/30/2023校赛总结/
作者
yech0
发布于
2023年11月30日
许可协议