First, it prints the string “password: “. Then, it allocates 0x100 bytes from the stack and reads up to 0x32 bytes from stdin. Since the buffer size 0x100 and it only reads 0x32 bytes there is no buffer overflow. Next, it compares the first byte of our input to 0x78 which is ‘x’. If it is not equal to ‘x’, it prints the error message “Wrong!\n” and exits. Let’s assume that the first character is ‘x’. Then, it jumps to 0x600199.

1

2

3

4

5

6

7

8

9

10

11

LOAD:0000000000600199loc_600199:; CODE XREF: LOAD:0000000000600170j

LOAD:0000000000600199movrax,[rcx]

LOAD:000000000060019Cmovedi,0CB6h

LOAD:00000000006001A1xoresi,esi

LOAD:00000000006001A3

LOAD:00000000006001A3loc_6001A3:; CODE XREF: LOAD:00000000006001B0j

LOAD:00000000006001A3cmpesi,edi

LOAD:00000000006001A5jzshortloc_6001B2

LOAD:00000000006001A7xorbyteptrloc_6001B2[esi],al

LOAD:00000000006001AEincesi

LOAD:00000000006001B0jmpshortloc_6001A3

Next, it xors 0xCB6 bytes at 0x6001B2 with ‘x’. After xoring 0xCB6 bytes, it jumps to 0x6001B2.

This means the code is self-decrypting. It decrypts then executes the decrypted code. Let’s create an IDC script and define a decrypt function.

decrypt.idc

C

1

2

3

4

5

6

7

8

staticdecrypt(from,size,key){

autoi,x;

for(i=0;i<size;i=i+1){

x=Byte(from+i);

x=(x^key);

PatchByte(from+i,x);

}

}

Let’s call it from the IDA’s console.

1

IDC>decrypt(0x6001B2,0xCB6,0x78);

This is the decrypted code.

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

LOAD:00000000006001B2loc_6001B2:; CODE XREF: LOAD:00000000006001A5j

LOAD:00000000006001B2; DATA XREF: LOAD:00000000006001A7w

LOAD:00000000006001B2incrcx

LOAD:00000000006001B5cmpbyteptr[rcx],69h

LOAD:00000000006001B8jzshortloc_6001E1

LOAD:00000000006001BAmoveax,1

LOAD:00000000006001BFmovedi,1

LOAD:00000000006001C4movrsi,offsetaWrong; "Wrong!\n"

LOAD:00000000006001CEmovedx,8

LOAD:00000000006001D3syscall

LOAD:00000000006001D5moveax,3Ch

LOAD:00000000006001DAmovedi,1

LOAD:00000000006001DFsyscall

LOAD:00000000006001E1

LOAD:00000000006001E1loc_6001E1:; CODE XREF: LOAD:00000000006001B8j

LOAD:00000000006001E1movrax,[rcx]

LOAD:00000000006001E4movedi,0C6Eh

LOAD:00000000006001E9xoresi,esi

LOAD:00000000006001EB

LOAD:00000000006001EBloc_6001EB:; CODE XREF: LOAD:00000000006001F8j

LOAD:00000000006001EBcmpesi,edi

LOAD:00000000006001EDjzshortloc_6001FA

LOAD:00000000006001EFxorbyteptrloc_6001FA[esi],al

LOAD:00000000006001F6incesi

LOAD:00000000006001F8jmpshortloc_6001EB

It is very similar to the previous one. It increases the pointer to get the next character of our input and this time it checks whether it is 0x69 which is ‘i’ and if it is correct, then it xors the 0xC6E bytes at 0x6001FA with it. Finally, it jumps to 0x6001FA. Let’s decrypt it as well.

1

decrypt(0x6001FA,0xC6E,0x69);

Let’s see the decrypted code snippet.

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

LOAD:00000000006001FAloc_6001FA:; CODE XREF: LOAD:00000000006001EDj

LOAD:00000000006001FA; DATA XREF: LOAD:00000000006001EFw

LOAD:00000000006001FAincrcx

LOAD:00000000006001FDcmpbyteptr[rcx],6Fh

LOAD:0000000000600200jzshortloc_600229

LOAD:0000000000600202moveax,1

LOAD:0000000000600207movedi,1

LOAD:000000000060020Cmovrsi,offsetaWrong; "Wrong!\n"

LOAD:0000000000600216movedx,8

LOAD:000000000060021Bsyscall

LOAD:000000000060021Dmoveax,3Ch

LOAD:0000000000600222movedi,1

LOAD:0000000000600227syscall

LOAD:0000000000600229

LOAD:0000000000600229loc_600229:; CODE XREF: LOAD:0000000000600200j

LOAD:0000000000600229movrax,[rcx]

LOAD:000000000060022Cmovedi,0C26h

LOAD:0000000000600231xoresi,esi

LOAD:0000000000600233

LOAD:0000000000600233loc_600233:; CODE XREF: LOAD:0000000000600240j

LOAD:0000000000600233cmpesi,edi

LOAD:0000000000600235jzshortnearptrbyte_600242

LOAD:0000000000600237xorbyte_600242[esi],al

LOAD:000000000060023Eincesi

LOAD:0000000000600240jmpshortloc_600233

Again, it is the same routine. This time our expected character is 0x6F which is ‘o’, the target is 0x600242, size is 0xC26. It decrypts the target and jumps to there. So, we have a pattern here. The decryption keys are the flag’s characters. Let’s reload the executable into IDA and create an IDC script that automatically decrypts all the code snippets and prints the flag to the output console.

Here is the IDC script to get the flag.

solve.idc

C

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

staticdecrypt(from,size,key){

autoi,x;

for(i=0;i<size;i=i+1){

x=Byte(from+i);

x=(x^key);

PatchByte(from+i,x);

}

}

staticgetFlag(){

autoaddr,size,key;

addr=0x6001B2;

size=0xCB6;

key=0x78;

while(1){

Message(key);

if(key=='}'){

break;

}

decrypt(addr,size,key);

key=Byte(addr+5);

size=Word(addr+51);

addr=Dword(addr+64);

}

}

Let’s call the getFlag function from the IDA console.

1

2

IDC>getFlag();

xiomara{cool_thumbs_up_if_solved_using_r2pipe}

This is the last decrypted snippet of the code.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

LOAD:0000000000600E12loc_600E12:; CODE XREF: LOAD:0000000000600E05j

LOAD:0000000000600E12; DATA XREF: LOAD:0000000000600E07w

LOAD:0000000000600E12incrcx

LOAD:0000000000600E15cmpbyteptr[rcx],7Dh

LOAD:0000000000600E18jzshortloc_600E41

LOAD:0000000000600E1Amoveax,1

LOAD:0000000000600E1Fmovedi,1

LOAD:0000000000600E24movrsi,offsetaWrong; "Wrong!\n"

LOAD:0000000000600E2Emovedx,8

LOAD:0000000000600E33syscall

LOAD:0000000000600E35moveax,3Ch

LOAD:0000000000600E3Amovedi,1

LOAD:0000000000600E3Fsyscall

LOAD:0000000000600E41

LOAD:0000000000600E41loc_600E41:; CODE XREF: LOAD:0000000000600E18j

LOAD:0000000000600E41moveax,1

LOAD:0000000000600E46movedi,1

LOAD:0000000000600E4Bmovrsi,offsetaGoodJob; "Good job!\n"

LOAD:0000000000600E55movedx,0Bh

LOAD:0000000000600E5Asyscall

LOAD:0000000000600E5Cmoveax,3Ch

LOAD:0000000000600E61movedi,0

LOAD:0000000000600E66syscall

LOAD:0000000000600E66LOADends

I’d like to mention that there was no length check for the password. Even though the flag is 46 characters, it reads up to 50 characters and only checks the first 46 characters. Thus, we can append any 4 characters to the flag and the password will still be correct.