9447 CTF 2014: Rolling write-up

Created:2014.12.04, last modified: 2014.12.04 by xorpse
Status: Complete

The challenge binary upon being executed presents a message in Welsh:

$ ./rolling
Fynd i mewn i cyfrinair

…which translates roughly to: Enter a password. Since there is no clear means of providing such password to the binary via standard input, we load it into IDA to see how to proceed.

The main function is quite short and we see that input is taken via the arguments passed to the binary (argv):

.text:0000000000400744                 push    rbp
.text:0000000000400745                 mov     rbp, rsp
.text:0000000000400748                 sub     rsp, 10h
.text:000000000040074C                 mov     [rbp+argc], edi
.text:000000000040074F                 mov     [rbp+argv], rsi
.text:0000000000400753                 cmp     [rbp+argc], 2
.text:0000000000400757                 jnz     short loc_40079D
.text:0000000000400759                 mov     rax, [rbp+argv]
.text:000000000040075D                 add     rax, 8
.text:0000000000400761                 mov     rax, [rax]      ; argv[1]
.text:0000000000400764                 movzx   eax, byte ptr [rax] ; argv[1][0]
.text:0000000000400767                 movsx   eax, al
.text:000000000040076A                 mov     edi, eax
.text:000000000040076C                 call    sub_4006C7
.text:0000000000400771                 mov     rdx, [rbp+argv]
.text:0000000000400775                 add     rdx, 8
.text:0000000000400779                 mov     rdx, [rdx]
.text:000000000040077C                 mov     rdi, rdx
.text:000000000040077F                 call    rax
.text:0000000000400781                 test    eax, eax
.text:0000000000400783                 jz      short loc_400791
.text:0000000000400785                 mov     edi, offset s   ; Congratulations.
.text:000000000040078A                 call    _puts
.text:000000000040078F                 jmp     short loc_4007A7
.text:0000000000400791 ; ---------------------------------------------------------------------------
.text:0000000000400791 loc_400791:                             ; CODE XREF: sub_400744+3Fj
.text:0000000000400791                 mov     edi, offset aNacOes_Ceisiwc ; No. Try again.
.text:0000000000400796                 call    _puts
.text:000000000040079B                 jmp     short loc_4007A7
.text:000000000040079D ; ---------------------------------------------------------------------------
.text:000000000040079D loc_40079D:                             ; CODE XREF: sub_400744+13j
.text:000000000040079D                 mov     edi, offset aFyndIMewnICyfr ; "Fynd i mewn i cyfrinair"
.text:00000000004007A2                 call    _puts
.text:00000000004007A7 loc_4007A7:                             ; CODE XREF: sub_400744+4Bj
.text:00000000004007A7                                         ; sub_400744+57j
.text:00000000004007A7                 mov     eax, 0
.text:00000000004007AC                 leave
.text:00000000004007AD                 retn
.text:00000000004007AD sub_400744      endp

It takes a single argument, of which the first character is passed to sub_4006C7. Looking at where the routine branches after sub_4006C7, we see sub_4006C7 must return a function pointer which, when called, shall take the input password and return non-zero in order to reach the ‘Congratulations’ message.

sub_4006C7 branches on the first character of the password: depending on if the character is a decimal digit or not:

.text:00000000004006C7                 push    rbp
.text:00000000004006C8                 mov     rbp, rsp
.text:00000000004006CB                 sub     rsp, 20h
.text:00000000004006CF                 mov     eax, edi
.text:00000000004006D1                 mov     [rbp-14h], al   ; char
.text:00000000004006D4                 mov     [rbp+code], 0
.text:00000000004006DC                 cmp     [rbp+char], '/'
.text:00000000004006E0                 jle     short loc_400715
.text:00000000004006E2                 cmp     [rbp+char], '9'
.text:00000000004006E6                 jg      short loc_400715
.text:00000000004006E8 if (c >= '0' && c <= '9')
.text:00000000004006E8                 mov     esi, 87Ch
.text:00000000004006ED                 mov     edi, offset loc_601060
.text:00000000004006F2                 call    loadCode
.text:00000000004006F7                 mov     [rbp+code], rax
.text:00000000004006FB                 movsx   eax, [rbp+char]
.text:00000000004006FF                 lea     edx, [rax-55]
.text:0000000000400702                 mov     rax, [rbp+code]
.text:0000000000400706                 mov     esi, 87Ch       ; code size
.text:000000000040070B                 mov     rdi, rax
.text:000000000040070E                 call    decode
.text:0000000000400713                 jmp     short loc_40073E
.text:0000000000400715 ; ---------------------------------------------------------------------------
.text:0000000000400715 if (!(c >= '0' && c <= '9'))
.text:0000000000400715 loc_400715:                             ; CODE XREF: sub_4006C7+19j
.text:0000000000400715                                         ; sub_4006C7+1Fj
.text:0000000000400715                 mov     esi, 874h
.text:000000000040071A                 mov     edi, offset unk_6018E0
.text:000000000040071F                 call    loadCode
.text:0000000000400724                 mov     [rbp-8], rax
.text:0000000000400728                 mov     rax, [rbp+code]
.text:000000000040072C                 mov     edx, 2
.text:0000000000400731                 mov     esi, 874h       ; code size
.text:0000000000400736                 mov     rdi, rax
.text:0000000000400739                 call    decode
.text:000000000040073E loc_40073E:                             ; CODE XREF: sub_4006C7+4Cj
.text:000000000040073E                 mov     rax, [rbp+code]
.text:0000000000400742                 leave
.text:0000000000400743                 retn
.text:0000000000400743 sub_4006C7      endp

Both unk_6018E0 and loc_601060 are pointers to data which are mapped as executable by loadCode. A pointer to said mapping is then (in both branches) passed to decode. decode takes as arguments: the mapping itself, the size of the mapping, and an additional integer:

.text:00000000004005DD                 push    rbp
.text:00000000004005DE                 mov     rbp, rsp
.text:00000000004005E1                 mov     [rbp+code], rdi
.text:00000000004005E5                 mov     [rbp+code_len], esi
.text:00000000004005E8                 mov     [rbp+var_20], edx
.text:00000000004005EB                 mov     [rbp+var_8], 0
.text:00000000004005F2                 mov     [rbp+var_8], 0
.text:00000000004005F9                 jmp     short loc_40065A
.text:00000000004005FB ; ---------------------------------------------------------------------------
.text:00000000004005FB loc_4005FB:                             ; CODE XREF: decode+86j
.text:00000000004005FB                 mov     eax, [rbp+code_len]
.text:00000000004005FE                 sub     eax, 1
.text:0000000000400601                 mov     [rbp+var_4], eax
.text:0000000000400604                 jmp     short loc_400650
.text:0000000000400606 ; ---------------------------------------------------------------------------
.text:0000000000400606 loc_400606:                             ; CODE XREF: decode+77j
.text:0000000000400606                 mov     eax, [rbp+var_4]
.text:0000000000400609                 cdqe
.text:000000000040060B                 lea     rdx, ds:0[rax*4]
.text:0000000000400613                 mov     rax, [rbp+code]
.text:0000000000400617                 add     rax, rdx
.text:000000000040061A                 mov     edx, [rbp+var_4]
.text:000000000040061D                 movsxd  rdx, edx
.text:0000000000400620                 lea     rcx, ds:0[rdx*4]
.text:0000000000400628                 mov     rdx, [rbp+code]
.text:000000000040062C                 add     rdx, rcx
.text:000000000040062F                 mov     ecx, [rdx]
.text:0000000000400631                 mov     edx, [rbp+var_4]
.text:0000000000400634                 movsxd  rdx, edx
.text:0000000000400637                 shl     rdx, 2
.text:000000000040063B                 lea     rsi, [rdx-4]
.text:000000000040063F                 mov     rdx, [rbp+code]
.text:0000000000400643                 add     rdx, rsi
.text:0000000000400646                 mov     edx, [rdx]
.text:0000000000400648                 xor     edx, ecx
.text:000000000040064A                 mov     [rax], edx
.text:000000000040064C                 sub     [rbp+var_4], 1
.text:0000000000400650 loc_400650:                             ; CODE XREF: decode+27j
.text:0000000000400650                 cmp     [rbp+var_4], 0
.text:0000000000400654                 jg      short loc_400606
.text:0000000000400656                 add     [rbp+var_8], 1
.text:000000000040065A loc_40065A:                             ; CODE XREF: decode+1Cj
.text:000000000040065A                 mov     eax, [rbp+var_20]
.text:000000000040065D                 sub     eax, 1
.text:0000000000400660                 cmp     eax, [rbp+var_8]
.text:0000000000400663                 jg      short loc_4005FB
.text:0000000000400665                 pop     rbp
.text:0000000000400666                 retn
.text:0000000000400666 decode          endp

A direct translation of decode results in a procedure that appears to overrun the size of of the given mapping. The following C code can be used to perform the decode function on a given buffer:

for (i = sizeof code / sizeof (unsigned int); i > 0; i--)
  code[idx] ^= code[idx - 1]

…which, when applied to the code loaded if the first character of the password is a digit gives the following disassembly when loaded into IDA:

seg000:0000000000000000                 push    rbp
seg000:0000000000000001                 mov     rbp, rsp
seg000:0000000000000004                 mov     [rbp-8], rdi
seg000:0000000000000008                 mov     rax, [rbp-8]
seg000:000000000000000C                 movzx   eax, byte ptr [rax]
seg000:000000000000000F                 cmp     al, 39h ; '9'
seg000:0000000000000011                 jnz     loc_217
seg000:0000000000000017                 mov     rax, [rbp-8]
seg000:000000000000001B                 add     rax, 1
seg000:000000000000001F                 movzx   eax, byte ptr [rax]
seg000:0000000000000022                 cmp     al, 34h ; '4'
seg000:0000000000000024                 jnz     loc_217
seg000:000000000000002A                 mov     rax, [rbp-8]
seg000:000000000000002E                 add     rax, 2
seg000:0000000000000032                 movzx   eax, byte ptr [rax]
seg000:0000000000000035                 cmp     al, 34h ; '4'
seg000:0000000000000037                 jnz     loc_217
seg000:000000000000003D                 mov     rax, [rbp-8]
seg000:0000000000000041                 add     rax, 3
seg000:0000000000000045                 movzx   eax, byte ptr [rax]
seg000:0000000000000048                 cmp     al, 37h ; '7'
seg000:000000000000004A                 jnz     loc_217
seg000:0000000000000050                 mov     rax, [rbp-8]
seg000:0000000000000054                 add     rax, 4
seg000:0000000000000058                 movzx   eax, byte ptr [rax]
seg000:000000000000005B                 movsx   edx, al
seg000:000000000000005E                 mov     rax, [rbp-8]
seg000:0000000000000062                 movzx   eax, byte ptr [rax]
seg000:0000000000000065                 movsx   eax, al
seg000:0000000000000068                 add     eax, 39h ; '9'
seg000:000000000000006B                 cmp     edx, eax
seg000:000000000000006D                 jnz     loc_217
seg000:0000000000000073                 mov     rax, [rbp-8]
seg000:0000000000000077                 add     rax, 5
seg000:000000000000007B                 movzx   eax, byte ptr [rax]
seg000:000000000000007E                 movsx   eax, al
seg000:0000000000000081                 mov     rdx, [rbp-8]
seg000:0000000000000085                 add     rdx, 1
seg000:0000000000000089                 movzx   edx, byte ptr [rdx]
seg000:000000000000008C                 movsx   edx, dl
seg000:000000000000008F                 add     edx, 3Bh ; ';'
seg000:0000000000000092                 cmp     eax, edx
seg000:0000000000000094                 jnz     loc_217
seg000:000000000000009A                 mov     rax, [rbp-8]
seg000:000000000000009E                 add     rax, 6
seg000:00000000000000A2                 movzx   eax, byte ptr [rax]
seg000:00000000000000A5                 movsx   eax, al
seg000:00000000000000A8                 mov     rdx, [rbp-8]
seg000:00000000000000AC                 add     rdx, 2
seg000:00000000000000B0                 movzx   edx, byte ptr [rdx]
seg000:00000000000000B3                 movsx   edx, dl
seg000:00000000000000B6                 add     edx, 38h ; '8'
seg000:00000000000000B9                 cmp     eax, edx
seg000:00000000000000BB                 jnz     loc_217
seg000:00000000000000C1                 mov     rax, [rbp-8]
seg000:00000000000000C5                 add     rax, 7
seg000:00000000000000C9                 movzx   eax, byte ptr [rax]
seg000:00000000000000CC                 movsx   eax, al
seg000:00000000000000CF                 mov     rdx, [rbp-8]
seg000:00000000000000D3                 add     rdx, 3
seg000:00000000000000D7                 movzx   edx, byte ptr [rdx]
seg000:00000000000000DA                 movsx   edx, dl
seg000:00000000000000DD                 add     edx, 35h ; '5'
seg000:00000000000000E0                 cmp     eax, edx
seg000:00000000000000E2                 jnz     loc_217
seg000:00000000000000E8                 mov     rax, [rbp-8]
seg000:00000000000000EC                 add     rax, 8
seg000:00000000000000F0                 movzx   eax, byte ptr [rax]
seg000:00000000000000F3                 movsx   eax, al
seg000:00000000000000F6                 mov     rdx, [rbp-8]
seg000:00000000000000FA                 add     rdx, 4
seg000:00000000000000FE                 movzx   edx, byte ptr [rdx]
seg000:0000000000000101                 movsx   edx, dl
seg000:0000000000000104                 sub     edx, 9
seg000:0000000000000107                 cmp     eax, edx
seg000:0000000000000109                 jnz     loc_217
seg000:000000000000010F                 mov     rax, [rbp-8]
seg000:0000000000000113                 add     rax, 9
seg000:0000000000000117                 movzx   eax, byte ptr [rax]
seg000:000000000000011A                 movsx   eax, al
seg000:000000000000011D                 mov     rdx, [rbp-8]
seg000:0000000000000121                 add     rdx, 5
seg000:0000000000000125                 movzx   edx, byte ptr [rdx]
seg000:0000000000000128                 movsx   edx, dl
seg000:000000000000012B                 sub     edx, 1
seg000:000000000000012E                 cmp     eax, edx
seg000:0000000000000130                 jnz     loc_217
seg000:0000000000000136                 mov     rax, [rbp-8]
seg000:000000000000013A                 add     rax, 0Ah
seg000:000000000000013E                 movzx   eax, byte ptr [rax]
seg000:0000000000000141                 movsx   eax, al
seg000:0000000000000144                 mov     rdx, [rbp-8]
seg000:0000000000000148                 add     rdx, 6
seg000:000000000000014C                 movzx   edx, byte ptr [rdx]
seg000:000000000000014F                 movsx   edx, dl
seg000:0000000000000152                 sub     edx, 5
seg000:0000000000000155                 cmp     eax, edx
seg000:0000000000000157                 jnz     loc_217
seg000:000000000000015D                 mov     rax, [rbp-8]
seg000:0000000000000161                 add     rax, 0Bh
seg000:0000000000000165                 movzx   eax, byte ptr [rax]
seg000:0000000000000168                 movsx   eax, al
seg000:000000000000016B                 mov     rdx, [rbp-8]
seg000:000000000000016F                 add     rdx, 7
seg000:0000000000000173                 movzx   edx, byte ptr [rdx]
seg000:0000000000000176                 movsx   edx, dl
seg000:0000000000000179                 sub     edx, 3
seg000:000000000000017C                 cmp     eax, edx
seg000:000000000000017E                 jnz     loc_217
seg000:0000000000000184                 mov     rax, [rbp-8]
seg000:0000000000000188                 add     rax, 0Ch
seg000:000000000000018C                 movzx   eax, byte ptr [rax]
seg000:000000000000018F                 movsx   eax, al
seg000:0000000000000192                 mov     rdx, [rbp-8]
seg000:0000000000000196                 add     rdx, 8
seg000:000000000000019A                 movzx   edx, byte ptr [rdx]
seg000:000000000000019D                 movsx   edx, dl
seg000:00000000000001A0                 add     edx, 0Ah
seg000:00000000000001A3                 cmp     eax, edx
seg000:00000000000001A5                 jnz     short loc_217
seg000:00000000000001A7                 mov     rax, [rbp-8]
seg000:00000000000001AB                 add     rax, 0Dh
seg000:00000000000001AF                 movzx   eax, byte ptr [rax]
seg000:00000000000001B2                 movsx   eax, al
seg000:00000000000001B5                 mov     rdx, [rbp-8]
seg000:00000000000001B9                 add     rdx, 9
seg000:00000000000001BD                 movzx   edx, byte ptr [rdx]
seg000:00000000000001C0                 movsx   edx, dl
seg000:00000000000001C3                 sub     edx, 8
seg000:00000000000001C6                 cmp     eax, edx
seg000:00000000000001C8                 jnz     short loc_217
seg000:00000000000001CA                 mov     rax, [rbp-8]
seg000:00000000000001CE                 add     rax, 0Eh
seg000:00000000000001D2                 movzx   eax, byte ptr [rax]
seg000:00000000000001D5                 movsx   eax, al
seg000:00000000000001D8                 mov     rdx, [rbp-8]
seg000:00000000000001DC                 add     rdx, 0Ah
seg000:00000000000001E0                 movzx   edx, byte ptr [rdx]
seg000:00000000000001E3                 movsx   edx, dl
seg000:00000000000001E6                 add     edx, 0Eh
seg000:00000000000001E9                 cmp     eax, edx
seg000:00000000000001EB                 jnz     short loc_217
seg000:00000000000001ED                 mov     rax, [rbp-8]
seg000:00000000000001F1                 add     rax, 0Fh
seg000:00000000000001F5                 movzx   eax, byte ptr [rax]
seg000:00000000000001F8                 movsx   eax, al
seg000:00000000000001FB                 mov     rdx, [rbp-8]
seg000:00000000000001FF                 add     rdx, 0Bh
seg000:0000000000000203                 movzx   edx, byte ptr [rdx]
seg000:0000000000000206                 movsx   edx, dl
seg000:0000000000000209                 add     edx, 5
seg000:000000000000020C                 cmp     eax, edx
seg000:000000000000020E                 jnz     short loc_217
seg000:0000000000000210                 mov     eax, 1
seg000:0000000000000215                 jmp     short loc_21C
seg000:0000000000000217 ; ---------------------------------------------------------------------------
seg000:0000000000000217 loc_217:                                ; CODE XREF: seg000:0000000000000011j
seg000:0000000000000217                                         ; seg000:0000000000000024j ...
seg000:0000000000000217                 mov     eax, 0
seg000:000000000000021C loc_21C:                                ; CODE XREF: seg000:0000000000000215j
seg000:000000000000021C                 pop     rbp
seg000:000000000000021D                 retn
seg000:000000000000021E ; ---------------------------------------------------------------------------
seg000:000000000000021E                 push    rbp

To have the binary output the ‘Congratulations’ message, the function needs to output a non-zero value: looking at the above listing, we see that this is acheived at address 0000000000000210. Working through the password validation in order to arrive at said address produces the following ‘equations’:

9 => i[0]
4 => i[1]
4 => i[2]
7 => i[3]
r => i[4]  == i[0] + '9'
o => i[5]  == i[1] + ';'
l => i[6]  == i[2] + '8'
l => i[7]  == i[3] + '5'
i => i[8]  == i[4] - 9
n => i[9]  == i[5] - 1
g => i[10] == i[6] - 5
i => i[11] == i[7] - 3
s => i[12] == i[8] + 10
f => i[13] == i[9] - 8
u => i[14] == i[10] + 14
n => i[15] == i[11] + 5

…and a flag value of 9447{9447rollingisfun}.