TUCTF 2019 - Reverse Engineering Monday. December 16, 2019 - 17 mins Faker The first thing i do is try to run the binary and give some input
./faker
From the result we know that there is no one output is the real flag , so the next thing i do is try to decompile it with ghidra.
void main ( void )
{
int iVar1 ;
long in_FS_OFFSET ;
char local_38 [ 40 ];
undefined8 local_10 ;
local_10 = * ( undefined8 * )( in_FS_OFFSET + 0x28 );
setvbuf ( stdout ,( char * ) 0x0 , 2 , 0x14 );
setvbuf ( stdin ,( char * ) 0x0 , 2 , 0x14 );
do {
printMenu ();
read ( 0 , local_38 , 0x20 );
iVar1 = atoi ( local_38 );
if ( iVar1 == 4 ) {
/* WARNING: Subroutine does not return */
exit ( 0 );
}
if ( iVar1 < 5 ) {
if ( iVar1 == 3 ) {
C ();
}
else {
if ( 3 < iVar1 ) goto LAB_0010151c ;
if ( iVar1 == 1 ) {
A ();
}
else {
if ( iVar1 != 2 ) goto LAB_0010151c ;
B ();
}
}
}
else {
LAB_0010151c:
puts ( "Unknown Input" );
}
puts ( "" );
} while ( true );
}
From pseudocode we know that when we input >=5 the binary will print “Unknown Input” and 1,2,3 is fake flag also 4 is exit , so where is the flag?
So i try to list all function and there is a function called thisone and i try to open it
void thisone ( void )
{
printFlag ( " \\ PJ \\ fC|)L0LTw@Yt@;Twmq0Lw|qw@w2$a@0;w|)@awmLL|Tw|)LwZL2lhhL0k" );
return ;
}
and here is the printFlag function
void printFlag ( char * pcParm1 )
{
char * __dest ;
size_t sVar1 ;
int local_30 ;
__dest = ( char * ) malloc ( 0x40 );
memset ( __dest , 0 , 0x40 );
strcpy ( __dest , pcParm1 );
sVar1 = strlen ( __dest );
local_30 = 0 ;
while ( local_30 < ( int ) sVar1 ) {
__dest [( long ) local_30 ] =
( char )(( int )(((( int ) __dest [( long ) local_30 ] ^ 0xfU ) - 0x1d ) * 8 ) % 0x5f ) + ' ' ;
local_30 = local_30 + 1 ;
}
puts ( __dest );
return ;
}
Now we have two solution to solve this challenge, first call the thisone function or decode the cipher text ( flag ) with the printFlag algorithm
1. Call thisone function Here we will use gdb to call thisone function , so first open the binary with gdb
gdb -q faker
After run this gdb command to call thisone function
b main
r
jump thisone
That commands mean first breakpoint at the beginning main function after that run the binary with that breakpoint and then jump to thisone function ( call thisone function ). Here is the result :
2. Decode with printFlag algorithm In this section i wil convert that pseudocode to python script and here is the script
pcParm1 = ' \\ PJ \\ fC|)L0LTw@Yt@;Twmq0Lw|qw@w2$a@0;w|)@awmLL|Tw|)LwZL2lhhL0k'
__dest = [ i for i in pcParm1 ]
sVar1 = len ( __dest )
local_30 = 0
while ( local_30 < sVar1 ):
__dest [ local_30 ] = chr ((((( ord ( __dest [ local_30 ]) ^ 0xf ) - 0x1d ) * 8 ) % 0x5f ) + ord ( ' ' ))
local_30 = local_30 + 1
print "" . join ( __dest )
Flag : TUCTF{7h3r35_4lw4y5_m0r3_70_4_b1n4ry_7h4n_m3375_7h3_d3bu663r} core Given file run.c and core where core cannot be run , so the first thing i do is try to open run.c to know the program flow
#include <stdio.h> // prints
#include <stdlib.h> // malloc
#include <string.h> // strcmp
#include <unistd.h> // read
#include <fcntl.h> // open
#include <unistd.h> // close
#include <time.h> // time
#define FLAG_LEN 64
char flag [ FLAG_LEN ];
void xor ( char * str , int len ) {
for ( int i = 0 ; i < len ; i ++ ) {
str [ i ] = str [ i ] ^ 1 ;
}
}
int main () {
setvbuf ( stdout , NULL , _IONBF , 20 );
setvbuf ( stdin , NULL , _IONBF , 20 );
// Read the flag
memset ( flag , 0 , FLAG_LEN );
printf ( "> " );
int len = read ( 0 , flag , FLAG_LEN );
xor ( flag , len );
char buf [ 32 ];
read ( 0 , buf , 128 );
return 0 ;
}
From the source code we know that there is operation where the flag is xoring by 1 , because we know format flag is TUCTF{} so we can find xored flag .
Python 2.7.12 (default, Oct 8 2019, 14:14:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a='TUCTF'
>>> b=''
>>> for i in a:
... b+=chr(ord(i)^1)
...
>>> b
'UTBUG'
After we know the xored flag then we strings and grep core file with xored flag
strings core | grep UTBUG
Here the result
After that just xor it with 1 to get the real flag
Python 2.7.12 (default, Oct 8 2019, 14:14:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a='UTBUGzb1s2^etlq>^O2w2s^i25se^1g^x1t|'
>>> flag=''
>>> for i in a:
... flag+=chr(ord(i)^1)
...
>>> flag
'TUCTF{c0r3_dump?_N3v3r_h34rd_0f_y0u}'
Flag : TUCTF{c0r3_dump?_N3v3r_h34rd_0f_y0u} Object Given file run.o and cannot be run , so i try to open it with ghidra
void main ( void )
{
char cVar1 ;
byte bVar2 ;
char * pcVar3 ;
char cVar4 ;
undefined2 uVar5 ;
long lVar6 ;
byte extraout_DL ;
char unaff_BH ;
long lVar7 ;
undefined8 local_10 ;
lVar6 = 0x14 ;
lVar7 = stdout ;
pcVar3 = ( char * ) func_0x00201115 ( stdout , 0 , 2 );
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
* ( char * )( lVar7 + lVar6 ) = * ( char * )( lVar7 + lVar6 ) + unaff_BH ;
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
uVar5 = 0x14 ;
pcVar3 = ( char * ) func_0x00201133 ( pcVar3 , 0 , 2 );
cVar4 = ( char ) uVar5 ;
cVar1 = ( char ) pcVar3 ;
* pcVar3 = * pcVar3 + cVar1 ;
* pcVar3 = * pcVar3 + cVar1 ;
bVar2 = cVar1 + ( char )(( ushort ) uVar5 >> 8 );
_bVar2 = ( byte * )(( ulong ) pcVar3 & 0xffffffffffffff00 | ( ulong ) bVar2 );
* _bVar2 = * _bVar2 + bVar2 + ( * _bVar2 < extraout_DL );
* _bVar2 = * _bVar2 + bVar2 ;
* _bVar2 = * _bVar2 + bVar2 ;
pcVar3 = ( char * ) func_0x0020116f ( local_10 , 0 , 0x40 );
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
* pcVar3 = * pcVar3 + unaff_BH + cVar4 ;
pcVar3 = ( char * ) func_0x00201158 ();
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
pcVar3 = ( char * ) func_0x002011a0 ( & DAT_00100218 , pcVar3 );
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
pcVar3 = ( char * ) func_0x00201188 ( "pass: %s \n " , pcVar3 );
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
* pcVar3 = * pcVar3 + ( char ) pcVar3 ;
return ;
}
There is two function , main and checkPassword . I think main function only get the input and after that pass it to checkPassword function .
/* WARNING: Instruction at (ram,0x001000a6) overlaps instruction at (ram,0x001000a5)
*/
void checkPassword ( long lParm1 , undefined8 uParm2 , undefined8 uParm3 , undefined8 uParm4 )
{
byte * pbVar1 ;
char cVar2 ;
byte bVar3 ;
char * pcVar4 ;
int * piVar5 ;
char cVar6 ;
char unaff_BL ;
undefined7 unaff_00000019 ;
uint local_10 ;
int local_c ;
cVar6 = ( char )(( ulong ) uParm4 >> 8 );
pcVar4 = ( char * ) func_0x00201024 ( lParm1 );
* pcVar4 = * pcVar4 + ( char ) pcVar4 ;
* pcVar4 = * pcVar4 + ( char ) pcVar4 ;
if ( local_c == ( int ) pcVar4 + 0x15b ) {
local_10 = 0 ;
}
else {
piVar5 = ( int * ) func_0x00201046 ( "Close, but no flag" );
cVar2 = ( char ) piVar5 ;
* ( char * ) piVar5 = * ( char * ) piVar5 + cVar2 ;
* ( char * ) piVar5 = * ( char * ) piVar5 + cVar2 ;
* piVar5 = * piVar5 + 0x45c70000 ;
* ( char * ) piVar5 = * ( char * ) piVar5 + cVar2 ;
* ( char * ) piVar5 = * ( char * ) piVar5 + cVar2 ;
}
while (( int ) local_10 < passlen ) {
if (( byte )( ~ ( * ( char * )( lParm1 + ( long )( int ) local_10 ) << 1 ) ^ 0xaaU ) ==
password [( long )( int ) local_10 ]) {
local_10 = local_10 + 1 ;
}
else {
pcVar4 = ( char * ) func_0x002010b2 ( "Incorrect password \n Error at character: %d \n " ,
( ulong ) local_10 );
bVar3 = ( byte ) pcVar4 ;
* pcVar4 = * pcVar4 + bVar3 ;
* pcVar4 = * pcVar4 + bVar3 ;
unaff_BL = unaff_BL + cVar6 ;
pbVar1 = ( byte * )( CONCAT71 ( unaff_00000019 , unaff_BL ) + - 0x74fe07bb );
* pbVar1 = * pbVar1 & bVar3 ;
}
}
pcVar4 = ( char * ) func_0x002010cc ( "Correct Password!" );
* pcVar4 = * pcVar4 + ( char ) pcVar4 ;
* pcVar4 = * pcVar4 + ( char ) pcVar4 ;
return ;
}
from pseudocode we know that there is checking on this line
while (( int ) local_10 < passlen ) {
if (( byte )( ~ ( * ( char * )( lParm1 + ( long )( int ) local_10 ) << 1 ) ^ 0xaaU ) ==
password [( long )( int ) local_10 ]) {
local_10 = local_10 + 1 ;
}
we know that lParm1 is our input and passlen is defined as 0000002Ch or 44 in decimal
//
// .data
// SHT_PROGBITS [ 0x180 - 0x183 ]
// ram: 00100180-00100183
//
passlen XREF [ 3 ]: Entry Point ( * ),
checkPassword: 001000 a5 ( R ),
_elfSectionHeaders: :000000 d0 ( * )
00100180 2 c 00 00 00 undefined4 0000002Ch
and here is the password value
//
// .rodata
// SHT_PROGBITS [ 0x188 - 0x226 ]
// ram: 00100188-00100226
//
DAT_00100188 XREF [ 3 ]: ch eckPassword : 0010006 b ( R ),
00100228(*),
_elfSectionHeaders: :00000150(*)
00100188 fd ?? FDh
00100189 ff ?? FFh
0010018 a d3 ?? D3h
0010018 b fd ?? FDh
0010018 c d9 ?? D9h
0010018 d a3 ?? A3h
0010018 e 93 ?? 93h
0010018 f 35 ?? 35h 5
00100190 89 ?? 89h
00100191 39 ?? 39h 9
00100192 b1 ?? B1h
00100193 3 d ?? 3Dh =
00100194 3 b ?? 3Bh ;
00100195 bf ?? BFh
00100196 8 d ?? 8Dh
00100197 3 d ?? 3Dh =
00100198 3 b ?? 3Bh ;
00100199 37 ?? 37h 7
0010019 a 35 ?? 35h 5
0010019 b 89 ?? 89h
0010019 c 3 f ?? 3Fh ?
0010019 d eb ?? EBh
0010019 e 35 ?? 35h 5
0010019 f 89 ?? 89h
001001 a0 eb ?? EBh
001001 a1 91 ?? 91h
001001 a2 b1 ?? B1h
001001 a3 33 ?? 33h 3
001001 a4 3 d ?? 3Dh =
001001 a5 83 ?? 83h
001001 a6 37 ?? 37h 7
001001 a7 89 ?? 89h
001001 a8 39 ?? 39h 9
001001 a9 eb ?? EBh
001001 aa 3 b ?? 3Bh ;
001001 ab 85 ?? 85h
001001 ac 37 ?? 37h 7
001001 ad 3 f ?? 3Fh ?
001001 ae eb ?? EBh
001001 af 99 ?? 99h
001001 b0 8 d ?? 8Dh
001001 b1 3 d ?? 3Dh =
001001 b2 39 ?? 39h 9
001001 b3 af ?? AFh
we can find the password in two ways , first bruteforce it ( because there is a check per index , so bruteforcing is ez for this challenge ) or reverse the check function , my solution in this challenge is reverse the function and here the solver
a = [ 0xFD , 0xFF , 0xD3 , 0xFD , 0xD9 , 0xA3 , 0x93 , 0x35 , 0x89 , 0x39 , 0xB1 , 0x3D , 0x3B , 0xBF , 0x8D , 0x3D , 0x3B , 0x37 , 0x35 , 0x89 , 0x3F , 0xEB , 0x35 , 0x89 , 0xEB , 0x91 , 0xB1 , 0x33 , 0x3D , 0x83 , 0x37 , 0x89 , 0x39 , 0xEB , 0x3B , 0x85 , 0x37 , 0x3F , 0xEB , 0x99 , 0x8D , 0x3D , 0x39 , 0xAF ]
flag = ""
for i in a :
flag += chr ((( 0xff - i ) ^ 0xAA ) / 2 )
print flag
Flag : TUCTF{c0n6r47ul4710n5_0n_br34k1n6_7h15_fl46} Author : kosong