[Reverse] Hackit 2017 - rev200


rev200

Description: You haxor, come on you little sciddie… debug me, eh? You fucking little lamer… You fuckin’ come on, come debug me! I’ll get your ass, you jerk! Oh, you IDA monkey! Fuck all you and your tools! Come on, you scum haxor, you try to reverse me? Come on, you asshole!!

Attachment: (none)

Webpage: https://mega.nz/#!RQJk0ZbR!7myppeUU6fwqRY2cOaX8EPTpC9pVIHEsWdUNw0szexc

Hint: (none)

When I looked up into this challenge I just checked the assembly code and found it way too easy for 200 points, but then I saw something weird I couldn’t start it for debugging and then I checked the file types:

1
2
$ file rev200\ \(1\).efi 
rev200 (1).efi: PE32+ executable (DLL) (EFI application) x86-64 (stripped to external PDB), for MS Windows

Oh its a ddl, that’s why well I still found it way to easy for all this points specially because with IDA we can convert this beautiful assembly code(missing the code for algo function):
Assembly IDA

Into this beautiful “pseudo c” code(press F5 in IDA), well it makes everything much easier even thought the assembly wasn’t that hard to reverse…. Here we have the pseudo c code for “main” function and “algo” function…

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
__int64 __fastcall efi_main(__int64 a1, __int64 a2)
{
UINTN v2; // rbx@3
char v4[48]; // [sp+20h] [bp-A0h]@1
__int16 v5; // [sp+48h] [bp-78h]@1
CHAR16 String[48]; // [sp+50h] [bp-70h]@1
int v7; // [sp+A0h] [bp-20h]@1
char v8; // [sp+B0h] [bp-10h]@4
int v9; // [sp+B8h] [bp-8h]@1
int i; // [sp+BCh] [bp-4h]@1
__int64 v11; // [sp+E8h] [bp+28h]@1

v11 = a2;
InitializeLib();
memset(String, 0, 0x50ui64);
v7 = 0;
memset(v4, 0, 0x28ui64);
v5 = 0;
v9 = 0;
Input(L"Enter the flag: ", String, 42i64);
for ( i = 0; ; ++i )
{
v2 = i;
if ( v2 >= StrLen(String) )
break;
v4[i] = String[i];
}
algo((__int64)v4);
(**(void (__fastcall ***)(_QWORD, _QWORD))(v11 + 48))(*(_QWORD *)(v11 + 48), 0i64);
(*(void (__fastcall **)(signed __int64, signed __int64, char *))(*(_QWORD *)(v11 + 96) + 96i64))(
1i64,
*(_QWORD *)(v11 + 48) + 16i64,
&v8);
return 0i64;
}

UINTN __fastcall algo(__int64 a1)
{
UINTN result; // rax@17
int v2[40]; // [sp+20h] [bp-60h]@11
int v3[20]; // [sp+C0h] [bp+40h]@5
int v4[23]; // [sp+110h] [bp+90h]@2
int m; // [sp+16Ch] [bp+ECh]@13
int l; // [sp+170h] [bp+F0h]@10
int k; // [sp+174h] [bp+F4h]@7
int j; // [sp+178h] [bp+F8h]@4
int i; // [sp+17Ch] [bp+FCh]@1

for ( i = 0; i <= 19; ++i )
v4[i] = *(_BYTE *)(i + a1);
for ( j = 20; j <= 39; ++j )
v3[j - 20] = *(_BYTE *)(j + a1);
for ( k = 0; k <= 19; ++k )
{
v4[k] = (((((v4[k] ^ 0xC) + 6) ^ 0xD) + 7) ^ 0xE) + 8;
v3[k] = (((((v3[k] ^ 0xF) + 9) ^ 0x10) + 10) ^ 0x11) + 11;
}
for ( l = 0; l <= 19; ++l )
v2[l] = v4[l];
for ( m = 20; m <= 39; ++m )
v2[m] = v3[m - 20];
if ( (unsigned int)memcmp((__int64)v2, (__int64)&correct, 160) )
result = Print(L"\nWrong\n");
else
result = Print(L"\nCorrect\n");
return result;
}

By reading this c code above we can clearly understand what is going on. the program itself reads from the stdout the flag and then encrypts a bunch of bytes with some xor operations and other stupid simple manipulations, since we know the ciphertext (we can get it from IDA) just double click on “correct” symbol from the main function and you will see its value in the global variables:

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
.data:0000000070946000                 public correct
.data:0000000070946000 correct db 68h ; h ; DATA XREF: algo+18Eo
.data:0000000070946001 db 0
.data:0000000070946002 db 0
.data:0000000070946003 db 0
.data:0000000070946004 db 3Ch ; <
.data:0000000070946005 db 0
.data:0000000070946006 db 0
.data:0000000070946007 db 0
.data:0000000070946008 db 79h ; y
.data:0000000070946009 db 0
.data:000000007094600A db 0
.data:000000007094600B db 0
.data:000000007094600C db 71h ; q
.data:000000007094600D db 0
.data:000000007094600E db 0
.data:000000007094600F db 0
.data:0000000070946010 db 63h ; c
.data:0000000070946011 db 0
.data:0000000070946012 db 0
.data:0000000070946013 db 0
.data:0000000070946014 db 7Ch ; |
.data:0000000070946015 db 0
.data:0000000070946016 db 0
.data:0000000070946017 db 0
.data:0000000070946018 db 81h ; ü
.data:0000000070946019 db 0
.data:000000007094601A db 0
.data:000000007094601B db 0
.data:000000007094601C db 92h ; Æ
.data:000000007094601D db 0
.data:000000007094601E db 0
.data:000000007094601F db 0
.data:0000000070946020 db 92h ; Æ
.data:0000000070946021 db 0
.data:0000000070946022 db 0
.data:0000000070946023 db 0
.data:0000000070946024 db 65h ; e
.data:0000000070946025 db 0
.data:0000000070946026 db 0
.data:0000000070946027 db 0
.data:0000000070946028 db 65h ; e
.data:0000000070946029 db 0
.data:000000007094602A db 0
.data:000000007094602B db 0
.data:000000007094602C db 93h ; ô
.data:000000007094602D db 0
.data:000000007094602E db 0
.data:000000007094602F db 0
.data:0000000070946030 db 92h ; Æ
.data:0000000070946031 db 0
.data:0000000070946032 db 0
.data:0000000070946033 db 0
.data:0000000070946034 db 49h ; I
.data:0000000070946035 db 0
.data:0000000070946036 db 0
.data:0000000070946037 db 0
.data:0000000070946038 db 79h ; y
.data:0000000070946039 db 0
.data:000000007094603A db 0
.data:000000007094603B db 0
.data:000000007094603C db 92h ; Æ
.data:000000007094603D db 0
.data:000000007094603E db 0
.data:000000007094603F db 0
.data:0000000070946040 db 38h ; 8
.data:0000000070946041 db 0
.data:0000000070946042 db 0
.data:0000000070946043 db 0
.data:0000000070946044 db 6Ch ; l
.data:0000000070946045 db 0
.data:0000000070946046 db 0
.data:0000000070946047 db 0
.data:0000000070946048 db 3Ch ; <
.data:0000000070946049 db 0
.data:000000007094604A db 0
.data:000000007094604B db 0
.data:000000007094604C db 6Fh ; o
.data:000000007094604D db 0
.data:000000007094604E db 0
.data:000000007094604F db 0
.data:0000000070946050 db 7Bh ; {
.data:0000000070946051 db 0
.data:0000000070946052 db 0
.data:0000000070946053 db 0
.data:0000000070946054 db 87h ; ç
.data:0000000070946055 db 0
.data:0000000070946056 db 0
.data:0000000070946057 db 0
.data:0000000070946058 db 58h ; X
.data:0000000070946059 db 0
.data:000000007094605A db 0
.data:000000007094605B db 0
.data:000000007094605C db 55h ; U
.data:000000007094605D db 0
.data:000000007094605E db 0
.data:000000007094605F db 0
.data:0000000070946060 db 89h ; ë
.data:0000000070946061 db 0
.data:0000000070946062 db 0
.data:0000000070946063 db 0
.data:0000000070946064 db 5Ah ; Z
.data:0000000070946065 db 0
.data:0000000070946066 db 0
.data:0000000070946067 db 0
.data:0000000070946068 db 59h ; Y
.data:0000000070946069 db 0
.data:000000007094606A db 0
.data:000000007094606B db 0
.data:000000007094606C db 7Eh ; ~
.data:000000007094606D db 0
.data:000000007094606E db 0
.data:000000007094606F db 0
.data:0000000070946070 db 7Eh ; ~
.data:0000000070946071 db 0
.data:0000000070946072 db 0
.data:0000000070946073 db 0
.data:0000000070946074 db 6Bh ; k
.data:0000000070946075 db 0
.data:0000000070946076 db 0
.data:0000000070946077 db 0
.data:0000000070946078 db 87h ; ç
.data:0000000070946079 db 0
.data:000000007094607A db 0
.data:000000007094607B db 0
.data:000000007094607C db 6Ch ; l
.data:000000007094607D db 0
.data:000000007094607E db 0
.data:000000007094607F db 0
.data:0000000070946080 db 57h ; W
.data:0000000070946081 db 0
.data:0000000070946082 db 0
.data:0000000070946083 db 0
.data:0000000070946084 db 6Ch ; l
.data:0000000070946085 db 0
.data:0000000070946086 db 0
.data:0000000070946087 db 0
.data:0000000070946088 db 6Bh ; k
.data:0000000070946089 db 0
.data:000000007094608A db 0
.data:000000007094608B db 0
.data:000000007094608C db 58h ; X
.data:000000007094608D db 0
.data:000000007094608E db 0
.data:000000007094608F db 0
.data:0000000070946090 db 59h ; Y
.data:0000000070946091 db 0
.data:0000000070946092 db 0
.data:0000000070946093 db 0
.data:0000000070946094 db 5Ah ; Z
.data:0000000070946095 db 0
.data:0000000070946096 db 0
.data:0000000070946097 db 0
.data:0000000070946098 db 5Ah ; Z
.data:0000000070946099 db 0
.data:000000007094609A db 0
.data:000000007094609B db 0
.data:000000007094609C db 6Fh ; o
.data:000000007094609D db 0
.data:000000007094609E db 0
.data:000000007094609F db 0

Now parsing it with sublime-txt(very fast and easy to do it), than we put into a hex byte characters string in python… why not put the characters since ida actually shows some of them? Because this is a ciphertext and some characters may be unprintable or those which can be printed may require unicode and we all know how boring is to deal with unicode in python … so I always use hex bytes.

1
correct = "\x68\x3C\x79\x71\x63\x7C\x81\x92\x92\x65\x65\x93\x92\x49\x79\x92\x38\x6C\x3C\x6F\x7B\x87\x58\x55\x89\x5A\x59\x7E\x7E\x6B\x87\x6C\x57\x6C\x6B\x58\x59\x5A\x5A\x6F"

Now I have an idea instead of trying to make a function that reverses those two functions why not bruteforce byte by byte, replicate the pseudo c code in python and when we get the final ciphertext equal to the “correct” variable we end the calculations and print the original string? seems like a cool plan! so we just rewrite that c code into python and then we add some loops to bruteforce the rest of the flag!!!

We don’t even need to bruteforce from the range from 0x00 to the 0xff we can just use printable characters since the flag will only have those! and more we already have the beginning and the end of the flag which is “h4ck1t{“ and “}”.

Here is the final payload:

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
from __future__ import print_function
import difflib
import sys
import string
from difflib import Differ


correct = "\x68\x3C\x79\x71\x63\x7C\x81\x92\x92\x65\x65\x93\x92\x49\x79\x92\x38\x6C\x3C\x6F\x7B\x87\x58\x55\x89\x5A\x59\x7E\x7E\x6B\x87\x6C\x57\x6C\x6B\x58\x59\x5A\x5A\x6F"

def algo(a1, offset):
result = ""
v2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
v3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
v4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

for i in xrange(20):
v4[i] = ord(a1[i])

for j in xrange(20,40):
v3[j-20]= ord(a1[j])

for k in xrange(0,20):
v4[k] = (((((v4[k] ^ 0xC) + 6) ^ 0xD) + 7) ^ 0xE) + 8
v3[k] = (((((v3[k] ^ 0xF) + 9) ^ 0x10) + 10) ^ 0x11) + 11
for l in xrange(0,20):
v2[l] = v4[l]
for m in xrange(20,40):
v2[m] = v3[m-20]
flag = ''.join([chr(x) for x in v2])
#print(flag)
#print(correct)

if(flag != correct):
result = "Wrong"
else:
result = "Correct"
return result, flag[offset] == correct[offset]

def main():
v2 = 0
v4 = ""
flag = "" # 0x20
v6 = ''
v7 = 0
i = 0
result = 'Wrong'
offset = 7
flag = "h4ck1t{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}"
diff = False
#print (string.printable)
#exit(0)
while result == 'Wrong':
#print offset
for y in string.printable:
v4 = ""
flag = [x for x in flag]
#print offset
flag[offset] = y
#print('\r'+''.join(flag),end='')
i = 0
while True:
v2 = i
if (v2 >= len(flag)):
break
v4 += flag[i]
i +=1
#print (v4,offset)
result, diff = algo(v4, offset)
if result == 'Correct':
break
#print (diff,y)
#print len(list(diff))
if diff:
diff = False
offset += 1
break
print(''.join(flag), result)
main()

Easy isn’t it? running it we get the flag!:

1
2
$ python rev200.py
h4ck1t{ff77af3cf8d4e1e67c4300aeb5ba6344} Correct