[Reverse] Tokyo Westerns CTF 3rd 2017 - Rev Rev Rev


Rev Rev Rev
Problem

rev_rev_rev

Lets first disassemble the main function of the binary:

main disas

Ignoring the MK_FP function which is related to the stack canaries protection on the executable we can see the program is reading from the STDIN into s and then modifies 4 modifications on the string using 4 functions:


sub_80486B9(&s);
sub_80486DB(&s);
sub_8048738(&s);
sub_80487B2(&s);

Sub_80486B9 is just removing the new line in the end of the string by inserting a nullbyte on it (note that 10 or 0x0A in hex represents the \n):

1
2
3
4
5
6
7
8
char *__cdecl sub_80486B9(char *s)
{
char *result; // eax@1

result = strchr(s, 10);
*result = 0;
return result;
}

sub_80486DB is just reversing the string:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char *__cdecl sub_80486DB(char *s)
{
char v1; // ST17_1@2
char *result; // eax@3
char *v3; // [sp+8h] [bp-10h]@1
char *i; // [sp+Ch] [bp-Ch]@1

v3 = s;
for ( i = &s[strlen(s) - 1]; ; --i )
{
result = v3;
if ( v3 >= i )
break;
v1 = *v3;
*v3 = *i;
*i = v1;
++v3;
}
return result;
}

sub_8048738 is performing a bunch of operation on the characters of the string some AND, OR and shifts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl sub_8048738(char *a1)
{
char v1; // ST0B_1@2
unsigned __int8 v2; // ST0B_1@2
int result; // eax@3
char *i; // [sp+Ch] [bp-4h]@1

for ( i = a1; ; ++i )
{
result = (unsigned __int8)*i;
if ( !(_BYTE)result )
break;
v1 = 2 * (*i & 0x55) | (*i >> 1) & 0x55;
v2 = 4 * (v1 & 0x33) | (v1 >> 2) & 0x33;
*i = 16 * v2 | (v2 >> 4);
}
return result;
}

sub_80487B2 is flipping the bits of each byte using ~ operator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl sub_80487B2(_BYTE *a1)
{
int result; // eax@3
_BYTE *i; // [sp+Ch] [bp-4h]@1

for ( i = a1; ; ++i )
{
result = *i;
if ( !(_BYTE)result )
break;
*i = ~*i;
}
return result;
}

We could rewrite this functions in python and apply it into the cipher string to get the real flag back! but thats too much work why don’t we insert the ciphertext to the binary and check if it spits the flag? lets do this with radare2:

And we got the flag!!! Additionally I did a python script that interacts with radare2 using r2pipe which is a really cool python package, this script will automate what I did manually above doing a dynamic analysis:

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
import r2pipe
import struct

profile = """#!/usr/bin/rarun2
program=./rev_rev_rev
stdin=output
"""
with open('profile.r2','w+') as f:
print >>f, profile

buf = struct.pack ("31B", *[
0x41,0x29,0xd9,0x65,0xa1,0xf1,0xe1,0xc9,0x19,0x09,0x93,
0x13,0xa1,0x09,0xb9,0x49,0xb9,0x89,0xdd,0x61,0x31,0x69,
0xa1,0xf1,0x71,0x21,0x9d,0xd5,0x3d,0x15,0xd5])
with open('output','w+') as f:
print >>f, buf


r2=r2pipe.open('./rev_rev_rev')
r2.cmd("e dbg.profile=profile.r2")
r2.cmd("doo")
r2.cmd("db 0x804866D")
r2.cmd("dc")
ciphertext = r2.cmd("pcp 31@ 0x8048870")
print r2.cmd("psz @ (ebp-0x2d)")

Running it

1
2
$ python revrevrev.py 2>/dev/null 
TWCTF{qpzisyDnbmboz76oglxpzYdk}