View the book with " Book Mode".
file
we see PIE
executable, this means that
reverse engineering
obscure function
PIE
parallels@parallels-vm:~/Desktop/MoveOrNot$ ./pro.dms
First give me your password: 1
You don't know static analysis !
Firstly, it asks you to enter the password, if a wrong password is given, it mocks you and end.
parallels@parallels-vm:~/Desktop/MoveOrNot$ ./pro.dms
First give me your password: 98416
Second give me your key: 50
Then Verify your flag: ?
You don't know dynamic analysis !
Through, reverse engineering, we can easily figure out that the first password is 98416
. After this, it will ask you to enter the second key, and ask you to verify your flag at the end.
undefined8 main(void)
{
int iVar1;
long in_FS_OFFSET;
int option;
int count;
char try_flag [40];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
count = 0;
printf("First give me your password: ");
/* scanf("%d", an integer) */
scanf(&DAT_00100a06,&option);
if (option != 98416) {
puts("You don\'t know static analysis !");
/* WARNING: Subroutine does not return */
exit(0);
}
printf("Second give me your key: ");
/* scanf("%d", an integer) */
scanf(&DAT_00100a06,&option);
option = option + -49;
count = 0;
while (count < 0xc) {
*(char *)(FUN_00301020 + count) = (char)FUN_00301020[count] + (char)option;
count = count + 1;
}
FUN_00301020(&magicstring);
printf("Then Verify your flag: ");
scanf(&DAT_00100a63,try_flag);
iVar1 = strcmp((char *)&magicstring,try_flag);
if (iVar1 == 0) {
puts("You are right. Congratulations !!");
}
else {
puts("You don\'t know dynamic analysis !");
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
The code above has been sorted, so it should be easier to read.
Firstly, line 16
shows the first password.
Secondly, line 23
scans the second key with data type int
.
The key will be updated by substracting 49.
Here comes the funniest part.
while (count < 0xc) {
*(char *)(FUN_00301020 + count) = (char)FUN_00301020[count] + (char)option;
count = count + 1;
}
it modifies machine code somehow by add a certain value.
We left this behind for a second, and find that it call the modified function FUN_00301020(&magicstring);
ok … so what we got there? FUN_00301020()
/* WARNING: Control flow encountered bad instruction data */
void FUN_00301020(int param_1,undefined8 param_2,undefined8 param_3,undefined4 *param_4)
{
int *piVar1;
int in_EAX;
undefined4 in_register_00000004;
char *magic_string;
char unaff_R14B;
bool in_ZF;
char in_SF;
char in_OF;
if (!in_ZF && in_OF == in_SF) {
piVar1 = (int *)(CONCAT44(in_register_00000004,in_EAX) + -0x7cb7c3f9);
*piVar1 = *piVar1 + in_EAX;
*param_4 = 0x48480780;
magic_string = (char *)(ulong)(param_1 + 1);
*magic_string = *magic_string + 'o';
magic_string[1] = magic_string[1] + ';';
magic_string[2] = magic_string[2] + 'Y';
magic_string[3] = magic_string[3] + 'F';
magic_string[4] = magic_string[4] + -0x14;
magic_string[5] = magic_string[5] + '\b';
magic_string[6] = magic_string[6] + '\x01';
magic_string[7] = magic_string[7] + '1';
magic_string[8] = magic_string[8] + '\x18';
magic_string[9] = magic_string[9] + -9;
magic_string[10] = magic_string[10] + '\x11';
magic_string[0xb] = magic_string[0xb] + -5;
magic_string[0xc] = magic_string[0xc] + -0x45;
magic_string[0xd] = magic_string[0xd] + '\x1a';
magic_string[0xe] = magic_string[0xe] + '-';
magic_string[0xf] = magic_string[0xf] + -0xb;
magic_string[0x10] = magic_string[0x10] + 'U';
magic_string[0x11] = magic_string[0x11] + '\x18';
magic_string[0x12] = magic_string[0x12] + '\x19';
magic_string[0x13] = magic_string[0x13] + -0x3b;
magic_string[0x14] = magic_string[0x14] + -9;
magic_string[0x15] = magic_string[0x15] + '\b';
magic_string[0x16] = magic_string[0x16] + 'X';
return;
}
if (unaff_R14B == '\0' || SCARRY1(unaff_R14B,'\0') != unaff_R14B < '\0') {
/* WARNING: Bad instruction - Truncating control flow here */
halt_baddata();
}
/* WARNING: Bad instruction - Truncating control flow here */
halt_baddata();
}
I was confused by this segment in the first place
piVar1 = (int *)(CONCAT44(in_register_00000004,in_EAX) + -0x7cb7c3f9);
*piVar1 = *piVar1 + in_EAX;
*param_4 = 0x48480780;
it turns out that we don't and should not read this code, I'll explain this later.
The rest codes are quite easy to understand though
magic_string = (char *)(ulong)(param_1 + 1);
*magic_string = *magic_string + 'o';
magic_string[1] = magic_string[1] + ';';
magic_string[2] = magic_string[2] + 'Y';
magic_string[3] = magic_string[3] + 'F';
modify the magic_string
According to Ghidra
the magic_string
is at address 00301100
magicstring XREF[2]: main:001008d4(*),
main:00100912(*)
00301100 69 db 69h
00301101 41 ?? 41h A
00301102 78 ?? 78h x
00301103 55 ?? 55h U
00301104 2e ?? 2Eh .
00301105 7c ?? 7Ch |
00301106 26 ?? 26h &
00301107 33 ?? 33h 3
00301108 30 ?? 30h 0
00301109 0c ?? 0Ch
0030110a 29 ?? 29h )
...
OK. Let's pause and think
we got
magic_string
: a raw stringFUN_00301020
: modify magic_string
somehowLet's go back to FUN_00301020
and microscope it's assembly
00301020 7f 2e JG LAB_00301050
00301022 26 47 82 ADD R14B,0x0
c6 00
00301027 7f 06 JG LAB_0030102f
00301029 1f ?? 1Fh
0030102a 47 ?? 47h G
0030102b 82 ?? 82h
0030102c c7 ?? C7h
0030102d 01 ?? 01h
0030102e 80 ?? 80h
LAB_0030102f XREF[1]: 00301027(j)
0030102f 2f ?? 2Fh /
00301030 0b ?? 0Bh
00301031 48 ?? 48h H
00301032 83 ?? 83h
00301033 c7 ?? C7h
00301034 01 ?? 01h
00301035 80 ?? 80h
00301036 07 ?? 07h
00301037 0d ?? 0Dh
00301038 48 ?? 48h H
00301039 83 ?? 83h
0030103a c7 ?? C7h
0030103b 01 ?? 01h
0030103c 80 ?? 80h
0030103d 07 ?? 07h
0030103e 41 ?? 41h A
0030103f 48 ?? 48h H
00301040 83 ?? 83h
00301041 c7 ?? C7h
00301042 01 ?? 01h
00301043 80 ?? 80h
00301044 2f ?? 2Fh /
00301045 0d ?? 0Dh
00301046 48 ?? 48h H
00301047 83 ?? 83h
00301048 c7 ?? C7h
00301049 01 ?? 01h
0030104a 80 ?? 80h
0030104b 07 ?? 07h
0030104c 20 ?? 20h
0030104d 48 ?? 48h H
0030104e 83 ?? 83h
0030104f c7 ?? C7h
LAB_00301050 XREF[1]: 00301020(j)
00301050 01 80 07 ADD dword ptr [RAX + -0x7cb7c3f9],EAX
3c 48 83
00301056 c7 01 80 MOV dword ptr [RCX],0x48480780
07 48 48
0030105c 83 c7 01 ADD magic_string,0x1
0030105f 80 07 6f ADD byte ptr [magic_string],0x6f
00301062 48 83 c7 01 ADD magic_string,0x1
00301066 80 07 3b ADD byte ptr [magic_string],0x3b
00301069 48 83 c7 01 ADD magic_string,0x1
0030106d 80 07 59 ADD byte ptr [magic_string],0x59
00301070 48 83 c7 01 ADD magic_string,0x1
00301074 80 07 46 ADD byte ptr [magic_string],0x46
00301077 48 83 c7 01 ADD magic_string,0x1
0030107b 80 2f 14 SUB byte ptr [magic_string],0x14
0030107e 48 83 c7 01 ADD magic_string,0x1
00301082 80 07 08 ADD byte ptr [magic_string],0x8
00301085 48 83 c7 01 ADD magic_string,0x1
...
Note that the first 49 lines are non-sense … so it must be related to the obscure function mentioned above.
By analyzing the machine code below, get
examples:
83 c7 01 ADD magic_string,0x1
80 07 6f ADD byte ptr [magic_string],0x6f
48 83 c7 01 ADD magic_string,0x1
80 2f 14 SUB byte ptr [magic_string],0x14
features:
83 c7 01
80 07 ()
48 83 c7 01
80 2f 14
Does the non-sense codes meet this features after some operations?
The answer is: Yes, plus 1
from 00301020:
(all plus 1)
80 2f 27 SUB 0x27
48 83 c7 01 ADD, magic_string, 0x1
80 07 20 ADD 0x20
from 0030102e:
80 2f 0b SUB 0xb
48 83 c7 01 ADD, magic_string, 0x1
80 07 0d ADD 0xd
48 83 c7 01 ADD, magic_string, 0x1
80 07 41 ADD 0x41
48 83 c7 01 ADD, magic_string, 0x1
80 2f 0d SUB 0xd
48 83 c7 01 ADD, magic_string, 0x1
80 07 20 ADD 0x20
48 83 c7 01 ADD, magic_string, 0x1
80 07 3c ADD 0x3c
48 83 c7 01 ADD, magic_string, 0x1
80 07 48 ADD 0x48
It's at this point that we realize that the
while (count < 0xc) {
*(char *)(FUN_00301020 + count) = (char)FUN_00301020[count] + (char)option;
count = count + 1;
}
actually expect the option be 1
(so we should enter 50
).
Now we can come up with exploit.py
magic_string = [
0x69,
0x41,
0x78,
0x55,
0x2E,
0x7C,
0x26,
0x33,
0x30,
0x0C,
0x29,
0x20,
0x28,
0x48,
0x65,
0x68,
0x32,
0x47,
0x3A,
0x62,
0x64,
0x79,
0x52,
0x46,
0x3B,
0x0A,
0x4F,
0x59,
0x6E,
0x3D,
0x6C,
0x25,
0x00,
]
seasoning = [
-0x27,
0x20,
-0xb,
0xd,
0x41,
-0xd,
0x20,
0x3c,
0x48,
0x6f,
0x3b,
0x59,
0x46,
-0x14,
0x8,
0x1,
0x31,
0x18,
-0x9,
0x11,
-0x5,
-0x45,
0x1a,
0x2d,
-0xb,
0x55,
0x18,
0x19,
-0x3b,
-0x9,
0x8,
0x58,
]
"""
0030105f 80 07 6f ADD byte ptr [magic_string],0x6f
00301062 48 83 c7 01 ADD magic_string,0x1
00301066 80 07 3b ADD byte ptr [magic_string],0x3b
00301069 48 83 c7 01 ADD magic_string,0x1
0030106d 80 07 59 ADD byte ptr [magic_string],0x59
00301070 48 83 c7 01 ADD magic_string,0x1
00301074 80 07 46 ADD byte ptr [magic_string],0x46
00301077 48 83 c7 01 ADD magic_string,0x1
0030107b 80 2f 14 SUB byte ptr [magic_string],0x14
0030107e 48 83 c7 01 ADD magic_string,0x1
00301082 80 07 08 ADD byte ptr [magic_string],0x8
00301085 48 83 c7 01 ADD magic_string,0x1
00301089 80 07 01 ADD byte ptr [magic_string],0x1
0030108c 48 83 c7 01 ADD magic_string,0x1
00301090 80 07 31 ADD byte ptr [magic_string],0x31
00301093 48 83 c7 01 ADD magic_string,0x1
00301097 80 07 18 ADD byte ptr [magic_string],0x18
0030109a 48 83 c7 01 ADD magic_string,0x1
0030109e 80 2f 09 SUB byte ptr [magic_string],0x9
003010a1 48 83 c7 01 ADD magic_string,0x1
003010a5 80 07 11 ADD byte ptr [magic_string],0x11
003010a8 48 83 c7 01 ADD magic_string,0x1
003010ac 80 2f 05 SUB byte ptr [magic_string],0x5
003010af 48 83 c7 01 ADD magic_string,0x1
003010b3 80 2f 45 SUB byte ptr [magic_string],0x45
003010b6 48 83 c7 01 ADD magic_string,0x1
003010ba 80 07 1a ADD byte ptr [magic_string],0x1a
003010bd 48 83 c7 01 ADD magic_string,0x1
003010c1 80 07 2d ADD byte ptr [magic_string],0x2d
003010c4 48 83 c7 01 ADD magic_string,0x1
003010c8 80 2f 0b SUB byte ptr [magic_string],0xb
003010cb 48 83 c7 01 ADD magic_string,0x1
003010cf 80 07 55 ADD byte ptr [magic_string],0x55
003010d2 48 83 c7 01 ADD magic_string,0x1
003010d6 80 07 18 ADD byte ptr [magic_string],0x18
003010d9 48 83 c7 01 ADD magic_string,0x1
003010dd 80 07 19 ADD byte ptr [magic_string],0x19
003010e0 48 83 c7 01 ADD magic_string,0x1
003010e4 80 2f 3b SUB byte ptr [magic_string],0x3b
003010e7 48 83 c7 01 ADD magic_string,0x1
003010eb 80 2f 09 SUB byte ptr [magic_string],0x9
003010ee 48 83 c7 01 ADD magic_string,0x1
003010f2 80 07 08 ADD byte ptr [magic_string],0x8
003010f5 48 83 c7 01 ADD magic_string,0x1
003010f9 80 07 58 ADD byte ptr [magic_string],0x58
"""
flags = []
for m, s in zip(magic_string, seasoning):
flags.append(chr(m+s))
print(''.join(flags))
ztex@Ztexde-MBP ~/Desktop/MoveOrNot python exploit.py
BambooFox{dyn4mic_1s_4ls0_gr34t}
Flag: BambooFox{dyn4mic_1s_4ls0_gr34t}
We can also solve this by ltrace
.
ltrace is a program that simply runs the specified command until it
exits. It intercepts and records the dynamic library calls which are
called by the executed process and the signals which are received by
that process. It can also intercept and print the system calls
executed by the program.
Its use is very similar to strace(1).
ltrace shows parameters of invoked functions and system calls. To
determine what arguments each function has, it needs external
declaration of function prototypes. Those are stored in files called
prototype libraries--see ltrace.conf(5) for details on the syntax of
these files. See the section PROTOTYPE LIBRARY DISCOVERY to learn
how ltrace finds prototype libraries.
parallels@parallels-vm:~/Desktop/MoveOrNot$ ltrace ./pro.dms
printf("First give me your password: ") = 29
__isoc99_scanf(0x5578e53a3a06, 0x7fffd2555a38, 0x7f787d131780, 29First give me your password: 98416
) = 1
printf("Second give me your key: ") = 25
__isoc99_scanf(0x5578e53a3a06, 0x7fffd2555a38, 0x7f787d131780, 25Second give me your key: 50
) = 1
printf("Then Verify your flag: ") = 23
__isoc99_scanf(0x5578e53a3a63, 0x7fffd2555a40, 0x7f787d131780, 23Then Verify your flag: a
) = 1
strcmp("BambooFox{dyn4mic_1s_4ls0_gr34t}"..., "a") = -31
puts("You don't know dynamic analysis "...You don't know dynamic analysis !
) = 34
+++ exited (status 0) +++
if the second key is wrong, crash
parallels@parallels-vm:~/Desktop/MoveOrNot$ ltrace ./pro.dms
printf("First give me your password: ") = 29
__isoc99_scanf(0x55f18e10ba06, 0x7ffd7d515de8, 0x7fd811ecf780, 29First give me your password: 98416
) = 1
printf("Second give me your key: ") = 25
__isoc99_scanf(0x55f18e10ba06, 0x7ffd7d515de8, 0x7fd811ecf780, 25Second give me your key: 1
) = 1
--- SIGILL (Illegal instruction) ---
+++ killed by SIGILL +++