Icon 2018 CTF third challenge

The third challenge is a reverse engineering problem. The zipped package contains 3 files:

crackme_baby
crackme.py
run.sh

Python file contains definitions of simple math operations (add, sub, mul, div2, mod, inf). Let us disassemble crackme_baby file.

Main function shows us that 2 long variables are in play, d1 = 25052671110843108 and d2 = 16420105858350620421142892712. They are passed to calc_flag__object___object to calculate value for the flag. This value is hashed with sha256 and print in format flag{hashed_value}.

.0x000021c3      push rbp                    ; main(int argc, char** argv)
.0x000021c4      mov rbp, rsp
.0x000021c7      push rbx
.0x000021c8      sub rsp, 0x88
.0x000021cf      mov dword [local_84h], edi  ; argc
.0x000021d5      mov qword [local_90h], rsi  ; argv
.0x000021dc      mov rax, qword fs:[0x28]    ; [0x28:8]=0x6108 ; '('
.0x000021e5      mov qword [local_18h], rax
.0x000021e9      xor eax, eax
.0x000021eb      call sym.imp.Py_Initialize
.0x000021f0      mov edx, 0xa
.0x000021f5      mov esi, 0
.0x000021fa      lea rdi, qword str.25052671110843108 ; 0x2aa0 ; "25052671110843108"
.0x00002201      call sym.imp.PyLong_FromString       ; create python long object
.0x00002206      mov qword [local_78h], rax           ; here we have d1
.0x0000220a      mov edx, 0xa
.0x0000220f      mov esi, 0
.0x00002214      lea rdi, qword str.16420105858350620421142892712 ; 0x2ab2 ; "16420105858350620421142892712"
.0x0000221b      call sym.imp.PyLong_FromString
.0x00002220      mov qword [local_70h], rax           ; here we have d2
.0x00002224      mov rdx, qword [local_70h]
.0x00002228      mov rax, qword [local_78h]
.0x0000222c      mov rsi, rdx
.0x0000222f      mov rdi, rax
.0x00002232      call sym.calc_flag__object___object ; compute the value for the flag
.0x00002237      mov qword [local_68h], rax
.0x0000223b      lea rsi, qword str.flag:_flag ; 0x2ad0 ; "flag: flag{"  
.0x00002242      lea rdi, qword obj._ZSt4cout__GLIBCXX_3.4 ; 0x204020
.0x00002249      call sym.std::basic_ostream_char_std::char_traits_char___std::operator___std::char_traits_char___std::basic_ostream_char_std::char_traits_char____charconst
.0x0000224e      mov rbx, rax
.0x00002251      mov rax, qword [local_68h] ; get computed value
.0x00002255      mov rdi, rax
.0x00002258      call sym.imp.PyLong_AsLong ; convert Python object to long
.0x0000225d      mov rdx, rax
.0x00002260      lea rax, qword [local_60h]
.0x00002264      mov rsi, rdx
.0x00002267      mov rdi, rax
.0x0000226a      call sym.std::__cxx11::to_string_long ; long -> string
.0x0000226f      lea rax, qword [local_40h] ; buffer for sha256 result
.0x00002273      lea rdx, qword [local_60h] ; string with the computed long value
.0x00002277      mov rsi, rdx
.0x0000227a      mov rdi, rax
.0x0000227d      call sym.sha256_std::__cxx11::basic_string_char_std::char_traits_char__std::allocator_char
.0x00002282      lea rax, qword [local_40h] ; we will print sha256 hash
.0x00002286      mov rsi, rax
.0x00002289      mov rdi, rbx
.0x0000228c      call sym.std::basic_ostream_char_std::char_traits_char___std::operator___char_std::char_traits_char__std::allocator_char___std::basic_ostream_char_std::char_traits_char____std::__cxx11::basic_string_char_std::char_traits_char__std::allocator_char__const
.0x00002291      lea rsi, qword [0x00002adc] ; "}" 
.0x00002298      mov rdi, rax
.0x0000229b      call sym.std::basic_ostream_char_std::char_traits_char___std::operator___std::char_traits_char___std::basic_ostream_char_std::char_traits_char____charconst
.0x000022a0      mov rdx, rax
                  ...

So it's time to look closer at function calc_flag__object___object. First instructions just load crackme.py and import math functions:

.0x00001e83      push rbp                               
.0x00001e84      mov rbp, rsp                
.0x00001e87      sub rsp, 0x70               ; 'p'
.0x00001e8b      mov qword [local_68h], rdi  ; d1
.0x00001e8f      mov qword [local_70h], rsi  ; d2
.0x00001e93      lea rdi, qword str.crackme  ; 0x2a7f ; "crackme"
.0x00001e9a      call sym.imp.PyUnicode_FromString
.0x00001e9f      mov qword [local_48h], rax
.0x00001ea3      mov rax, qword [local_48h]
.0x00001ea7      mov rdi, rax                           
.0x00001eaa      call sym.imp.PyImport_Import
.0x00001eaf      mov qword [local_40h], rax             
.0x00001eb3      mov rax, qword [local_40h]
.0x00001eb7      mov rdi, rax
.0x00001eba      call sym.imp.PyModule_GetDict
.0x00001ebf      mov qword [local_38h], rax
.0x00001ec3      mov rax, qword [local_38h]
.0x00001ec7      lea rsi, qword [0x00002a87] ; "add"
.0x00001ece      mov rdi, rax
.0x00001ed1      call sym.imp.PyDict_GetItemString
.0x00001ed6      mov qword [local_30h], rax
.0x00001eda      mov rax, qword [local_38h]
.0x00001ede      lea rsi, qword [0x00002a8b] ; "sub"
.0x00001ee5      mov rdi, rax
.0x00001ee8      call sym.imp.PyDict_GetItemString
.0x00001eed      mov qword [local_28h], rax
.0x00001ef1      mov rax, qword [local_38h]
.0x00001ef5      lea rsi, qword [0x00002a8f] ; "mul"
.0x00001efc      mov rdi, rax
.0x00001eff      call sym.imp.PyDict_GetItemString
.0x00001f04      mov qword [local_20h], rax
.0x00001f08      mov rax, qword [local_38h]
.0x00001f0c      lea rsi, qword str.div2     ; 0x2a93 ; "div2"
.0x00001f13      mov rdi, rax
.0x00001f16      call sym.imp.PyDict_GetItemString
.0x00001f1b      mov qword [local_18h], rax
.0x00001f1f      mov rax, qword [local_38h]
.0x00001f23      lea rsi, qword [0x00002a98] ; "mod"
.0x00001f2a      mov rdi, rax
.0x00001f2d      call sym.imp.PyDict_GetItemString
.0x00001f32      mov qword [local_10h], rax
.0x00001f36      mov rax, qword [local_38h]
.0x00001f3a      lea rsi, qword [0x00002a9c] ; "sup"
.0x00001f41      mov rdi, rax
.0x00001f44      call sym.imp.PyDict_GetItemString
.0x00001f49      mov qword [local_8h], rax

From above code we can extract variables with functions imported and assigned to them:

 local_30h | add
 local_28h | sub
 local_20h | mul
 local_18h | div2
 local_10h | mod
 local_8h  | sup

and also our longs:

 local_68h | d1
 local_70h | d2

We will need this to find out what calculations take place in calc_flag__object___object. I will replace local_xxh with known or chosen names. This way the graph below should be easier to understand.

                                                        ...
                                   | mov qword [sup], rax                       |                               
                                   | mov edi, 2                                 |                               
                                   | call sym.imp.PyLong_FromLong;[ge]          |                               
                                   | mov qword [factor], rax                    |                               
                                   | mov edi, 1                                 |                               
                                   | call sym.imp.PyLong_FromLong;[ge]          |                               
                                   | mov qword [last_factor], rax               |                               
                                   `--------------------------------------------'                               
                                                               |                                                
                                                               |                                                
     .---------------------------------------------------------.                                                
     |                                                         |                                                
     |                                                         |                                                
     |                        .------------------------------------------------------------------.              
     |                        |  0x1f69 ;[gj]                                                    |              
     |                        |      ; JMP XREF from 0x0000200d (sym.calc_flag__object___object) |              
     |                        | mov edi, 1                                                       |              
     |                        | call sym.imp.PyLong_FromLong;[ge]                                |              
     |                        | mov rdx, rax                                                     |              
     |                        | mov rcx, qword [d1]                                              |              
     |                        | mov rax, qword [sup]                                             |              
     |                        | mov rsi, rcx                                                     |              
     |                        | mov rdi, rax                                                     |              
     |                        | call sym.call__object___object___object;[gg]                     | 
     |                        | mov rdi, rax                                                     |              
     |                        | call sym.imp.PyLong_AsLong;[gh]                                  |              
     |                        | test rax, rax                                                    |              
     |                        | setne al                                                         |              
     |                        | test al, al                                                      |              
     |                        | je 0x2012;[gi]                                                   |              
     |                        `------------------------------------------------------------------'              
     |                                                             | |                                          
     |                                                             | '------------.                             
     |                    .----------------------------------------'              |                             
     |.---------------.   |                                                       |                             
     ||               |   |                                                       |                             
     ||               |   |                                                       |                             
     ||  .-----------------------------------------------.  .-----------------------------------------------.  
     ||  |  0x1f9b ;[gl]                                 |  | [0x2012] ;[gi]                                |  
     ||  |      ; JMP XREF from 0x00001fe7               |  |      ; JMP XREF from 0x00001f99               | 
     ||  |      ;   (sym.calc_flag__object___object)     |  |      ;   (sym.calc_flag__object___object)     |
     ||  | mov rdx, qword [factor]                       |  | mov rdx, qword [last_factor]                  |  
     ||  | mov rcx, qword [d1]                           |  | mov rcx, qword [d2]                           |  
     ||  | mov rax, qword [mod]                          |  | mov rax, qword [sub]                          |  
     ||  | mov rsi, rcx                                  |  | mov rsi, rcx                                  |  
     ||  | mov rdi, rax                                  |  | mov rdi, rax                                  |  
     ||  | call sym.call__object___object___object;[gg]  |  | call sym.call__object___object___object;[gg]  |  
     ||  | mov rdi, rax                                  |  | leave                                         |  
     ||  | call sym.imp.PyLong_AsLong;[gh]               |  | ret                                           |  
     ||  | test rax, rax                                 |  `-----------------------------------------------'  
     ||  | sete al                                       |                                                                      
     ||  | test al, al                                   |                                                                      
     ||  | je 0x1fe9;[gk]                                |                                                                      
     ||  `-----------------------------------------------'                                                                      
     ||                   | |                                                                                                    
     ||                   | '------------------------------------.                                                               
     ||        .----------'                                      |                                                               
     ||        |                                                 |                                                               
     ||        |                                                 |                                                               
     ||.-----------------------------------------------.   .------------------------------------------------------------------.  
     |||  0x1fc4 ;[gm]                                 |   |  0x1fe9 ;[gk]                                                    |  
     ||| mov rdx, qword [factor]                       |   |      ; JMP XREF from 0x00001fc2 (sym.calc_flag__object___object) |  
     ||| mov rcx, qword [d1]                           |   | mov edi, 1                                                       |  
     ||| mov rax, qword [div2]                         |   | call sym.imp.PyLong_FromLong;[ge]                                |  
     ||| mov rsi, rcx                                  |   | mov rdx, rax                                                     |  
     ||| mov rdi, rax                                  |   | mov rcx, qword [factor]                                          |  
     ||| call sym.call__object___object___object;[gg]  |   | mov rax, qword [add]                                             |  
     ||| mov qword [d1], rax                           |   | mov rsi, rcx                                                     |  
     ||| mov rax, qword [factor]                       |   | mov rdi, rax                                                     |  
     ||| mov qword [last_factor], rax                  |   | call sym.call__object___object___object;[gg]                     |  
     ||| jmp 0x1f9b;[gl]                               |   | mov qword [factor], rax                                          |  
     ||`-----------------------------------------------'   | jmp 0x1f69;[gj]                                                  |  
     ||    |                                               `------------------------------------------------------------------'  
     ||    |                                                   |                                                                 
     |`----'                                                   |                                                    
     `---------------------------------------------------------' 

Calling python functions convention is to put the address of an imported function to rdi register and the address of python object with the first argument to rsi, rdx will have the second argument address. Then there is a call to sym.call__object___object___object, the result will be also python object which address will be in rax register.

The algorithm shown above could be described like this:

  1. Set factor = 2 and last_factor = 1.
  2. Next, we check if d1 is greater than 1. If not return d2 - last_factor.
  3. Test if factor divides d1 with no rest. If True then go to step 4 else step 5.
  4. d1 = d1 / factor and save the value of factor in last_factor and go to step 3.
  5. factor += 1 and go to step 2.

In other words, when condition d1 > 1 will not be fulfilled last_factor will hold the greatest prime factor of d1. And this value will be subtracted from d2 and the result will be returned from the function. As you might already suspect running this program will give us nothing. So we better write our faster version of it.

To get the greatest prime factor of d1 I will use primefac python library.

import primefac
import hashlib

arg1 = 25052671110843108
arg2 = 16420105858350620421142892712

fac = list( primefac.primefac(arg1) )
bigestPrime = fac[-1]
flag = arg2 - bigestPrime
flag_str = str(flag)
f = hashlib.sha256(flag_str.encode('utf-8')).hexdigest()
print(f)

And here we have the content of the flag.

...SQUEAK!

Comments

Comments powered by Disqus