Inequation Group deltok og vant Telenor Security Operation Centre sin CTF.

I denne blogposten skal vi ta en titt på hvordan vi kan løse oppgave 7.

Oppgave 7

Klaus har lagret en fil ved navn «crackme1» på sitt hjemmeområde. Den spør etter et passord, kan du finne passordet?

Vi kjører programmet file mot filen og ser at det er en 64 bit elf altså en linux programfil som ikke har blitt stripped  dette betyr at vi har detaljer om orignale funksjonnavn og slik i filen.

$file crackme1 
crackme1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked ...[SNIPPED]..., not stripped

Vi åpner så filen i radare2 med -A flagget slik at den automatisk analyserer filen som en programfil vi kjører så afl komandoen for å få en liste over funksjonene i programmet

$r2 -A crackme1 
...[SNIPPED]...
[0x000006a0]> afl
0x00000000    3 72   -> 73   sym.imp.__libc_start_main
0x00000618    3 23           sym._init
0x00000640    1 6            sym.imp.puts
0x00000650    1 6            sym.imp.__stack_chk_fail
0x00000660    1 6            sym.imp.printf
0x00000670    1 6            sym.imp.strcmp
0x00000680    1 6            sym.imp.__isoc99_scanf
0x00000690    1 6            sub.__cxa_finalize_690
0x000006a0    1 43           entry0
0x000006d0    4 50   -> 40   sym.deregister_tm_clones
0x00000710    4 66   -> 57   sym.register_tm_clones
0x00000760    4 49           sym.__do_global_dtors_aux
0x000007a0    1 10           entry1.init
0x000007aa    6 187          main
0x00000870    4 101          sym.__libc_csu_init
0x000008e0    1 2            sym.__libc_csu_fini
0x000008e4    1 9            sym._fini

Vi ser at main ganske så sikkert er den eneste ikke importerte funksjonen i programmet. Vi søker så til main  med s komandoen og så printer vi ut disassemblyen med pdf kommandoen p for å  printe d for å dissasemble og f for å spesifikkere at vi vil dissamblere en funksjon

[0x000006a0]> s main
[0x000007aa]> pdf
/ (fcn) main 187
|   main ();
|           ; var int local_54h @ rbp-0x54
|           ; var int local_50h @ rbp-0x50
|           ; var int local_48h @ rbp-0x48
|           ; var int local_40h @ rbp-0x40
|           ; var int local_30h @ rbp-0x30
|           ; var int local_8h @ rbp-0x8
|           ; DATA XREF from 0x000006bd (entry0)
|           0x000007aa      55             push rbp
|           0x000007ab      4889e5         mov rbp, rsp
|           0x000007ae      4883ec60       sub rsp, 0x60               
|           0x000007b2      64488b042528.  mov rax, qword fs:[0x28]    
|           0x000007bb      488945f8       mov qword [local_8h], rax
|           0x000007bf      31c0           xor eax, eax
|           0x000007c1      48b8536c6170.  movabs rax, 0x6361427070616c53
|           0x000007cb      48ba6f6e5061.  movabs rdx, 0x7a69506161506e6f
|           0x000007d5      488945b0       mov qword [local_50h], rax
|           0x000007d9      488955b8       mov qword [local_48h], rdx
|           0x000007dd      48b87a615375.  movabs rax, 0x7265677553617a
|           0x000007e7      488945c0       mov qword [local_40h], rax
|           0x000007eb      488d3d020100.  lea rdi, qword str.Enter_Password: "Enter Password: "
|           0x000007f2      b800000000     mov eax, 0
|           0x000007f7      e864feffff     call sym.imp.printf
|           0x000007fc      488d45d0       lea rax, qword [local_30h]
|           0x00000800      4889c6         mov rsi, rax
|           0x00000803      488d3dfb0000.  lea rdi, qword [0x00000905] ; "%s"
|           0x0000080a      b800000000     mov eax, 0
|           0x0000080f      e86cfeffff     call sym.imp.__isoc99_scanf
|           0x00000814      488d55b0       lea rdx, qword [local_50h]
|           0x00000818      488d45d0       lea rax, qword [local_30h]
|           0x0000081c      4889d6         mov rsi, rdx
|           0x0000081f      4889c7         mov rdi, rax
|           0x00000822      e849feffff     call sym.imp.strcmp
|           0x00000827      8945ac         mov dword [local_54h], eax
|           0x0000082a      837dac00       cmp dword [local_54h], 0
|       ,=< 0x0000082e      750e           jne 0x83e
|       |   0x00000830      488d3dd10000.  lea rdi, qword str.Success  ;  Success"
|       |   0x00000837      e804feffff     call sym.imp.puts
|      ,==< 0x0000083c      eb0c           jmp 0x84a
|      ||   ; JMP XREF from 0x0000082e (main)
|      |`-> 0x0000083e      488d3dcb0000.  lea rdi, qword str.Fail     ; "Fail!"
|      |    0x00000845      e8f6fdffff     call sym.imp.puts
|      |    ; JMP XREF from 0x0000083c (main)
|      `--> 0x0000084a      b800000000     mov eax, 0
|           0x0000084f      488b4df8       mov rcx, qword [local_8h]
|           0x00000853      6448330c2528.  xor rcx, qword fs:[0x28]
|       ,=< 0x0000085c      7405           je 0x863
|       |   0x0000085e      e8edfdffff     call sym.imp.__stack_chk_fail
|       |   ; JMP XREF from 0x0000085c (main)
|       `-> 0x00000863      c9             leave
\           0x00000864      c3             ret

Vi ser av dette at programmet aksepterer input fra standard inndata og at den skrivet dette til rbp-0x30, [local_30h]  i radar utskriften.

|           0x000007fc      488d45d0       lea rax, qword [local_30h]
|           0x00000800      4889c6         mov rsi, rax
|           0x00000803      488d3dfb0000.  lea rdi, qword [0x00000905] ; "%s"
|           0x0000080a      b800000000     mov eax, 0
|           0x0000080f      e86cfeffff     call sym.imp.__isoc99_scanf 

Dette vet vi siden RSI er det andre argumentet til funksjoner på grunn64 bit linux calling conventions dermed så kan vi forstå at  dette under tilsvarer koden over

scanf(rdi,rsi);

siden RDI pointer til stringen "%s" og RSI pointer til rbp-0x30 (local_30) så kan vi gjette at RBP-0x30 er et array 

og at disse 5 instruksjonene kan bli "oversatt" til scanf("%s",local_30);

Rett etter disse instruksjonene så  har vi noe kode før strcmp som fører til logikk som printer ut Congratiulations om de to tekststrengene er like.

|           0x00000814      488d55b0       lea rdx, qword [local_50h]
|           0x00000818      488d45d0       lea rax, qword [local_30h]
|           0x0000081c      4889d6         mov rsi, rdx
|           0x0000081f      4889c7         mov rdi, rax
|           0x00000822      e849feffff     call sym.imp.strcmp
|           0x00000827      8945ac         mov dword [local_54h], eax
|           0x0000082a      837dac00       cmp dword [local_54h], 0
|           0x0000082e      750e           jne 0x83e
|           0x00000830      488d3dd10000.  lea rdi, qword str.Success  ;  Success"
|           0x00000837      e804feffff     call sym.imp.puts

Vi ser at tekststrengen vi ga programmet blir lastet inn i rdi, det første argumentet i en funksjons call, og at denne blir sammenlignet med en annen string som er lagret i adressen rbp-0x50, [local_50] i radar utskriften. Vi kan "oversette" denne koden slik

char *rsi = local_50 ; // vi må anta at local_50 er en char * 
char *rdi = input; // [Local_30]
if(stcmp(input,local_50)==0)
{
	puts("Success");
}
// Jeg skippet  swappen mellom registere før callen

Flagget må være hva som er lagret i local_50, vi kan finne hva som er lagret der  enten ved å bruke statisk eller dynamisk analyse.

Statisk analyse

Vi ser at ved starten av programmet så blir local_50 satt til 0x6361427070616c53 dette nummeret består av ascii verdier,  vis vi konverteren dette til ascii så får vi caBppalS denne tekstrengen er baklengs  på grunn av Endianess. Rett etter dette så ser vi at noen andre lignende nummer blir plassert på stacken. Siden arrays fungerer baklengs i forhold til stacken så kan vi gjette at disse tilhører local_50

|           0x000007c1      48b8536c6170.  movabs rax, 0x6361427070616c53
|           0x000007cb      48ba6f6e5061.  movabs rdx, 0x7a69506161506e6f
|           0x000007d5      488945b0       mov qword [local_50h], rax
|           0x000007d9      488955b8       mov qword [local_48h], rdx
|           0x000007dd      48b87a615375.  movabs rax, 0x7265677553617a
|           0x000007e7      488945c0       mov qword [local_40h], rax

Siden stacken vokser backlengs så kan vi anta at den første stelen av stringen
lagret baklengs er 0x6361427070616c53 den andre delen er 0x7a69506161506e6f og den 
siste delen er 0x7265677553617a 

Vi kan lett skrive et python script for å dekodifiserer dette 

#!/usr/bin/python3

str = ""
str += bytes.fromhex('6361427070616c53').decode('ascii')[::-1] 
str += bytes.fromhex('7a69506161506e6f').decode('ascii')[::-1] 
str += bytes.fromhex('7265677553617a').decode('ascii')[::-1] 
print(str)

Dette Spytter ut flagget 'SlappBaconPaaPizzaSuger' 

Dynamisk analyse

Vi kan også se hva som ligger i rsi (det andre argumentet til funksjonen) når programmet caller strcmp dette er ganske så lett og vi kan gjøre dette med gdb. Vi starter med gdb -q [programfil], vi kjører så disassemble main kommandoen for å få disassemblyen av main slik at vi kan vite hvilket offset i main vi vil breake på. For å breake ved det offsettet skriver break *main+offset før vi kjører programet med r. Når vi når breakpointet så printer vi ut rsi med  x/s $rsi, x for å spesifisere at vi vil printe ut en adresse /s for å printe ut dette som en string

gdb -q ./crackme1
Reading symbols from ./crackme1...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel // jeg kjører dette for å få disambly i intel syntax
(gdb) disassemble main
Dump of assembler code for function main:
   0x00000000000007aa <+0>:	push   rbp
   0x00000000000007ab <+1>:	mov    rbp,rsp
   0x00000000000007ae <+4>:	sub    rsp,0x60
   ...[SNIPP]...
   0x0000000000000822 <+120>:	call   0x670 <strcmp@plt>
   0x0000000000000827 <+125>:	mov    DWORD PTR [rbp-0x54],eax
   ...[SNIPP]...
   0x0000000000000863 <+185>:	leave  
   0x0000000000000864 <+186>:	ret    
End of assembler dump.
(gdb) break *main+120
Breakpoint 1 at 0x822
(gdb) r
Starting program: /home/NSA_NOT_TODAY/TSOCCTF/crackme1 
Enter Password: asd

Breakpoint 1, 0x0000555555554822 in main ()
(gdb) i r rsi
rsi            0x7fffffffe180	140737488347520
(gdb) x/s $rsi
0x7fffffffe180:	"SlappBaconPaaPizzaSuger"

       

IDA

letteste måten å løse det hele er egentlig bare å bruke IDA PRO :), hvis man åpner filen i Ida PRO og søker til main så kan man bare trykke F5 og få IDA sin "oversettelse" av det hele.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s2[8]; // [rsp+10h] [rbp-50h]
  char s1; // [rsp+30h] [rbp-30h]
  unsigned __int64 v6; // [rsp+58h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  strcpy(s2, "SlappBaconPaaPizzaSuger");
  printf("Enter Password: ", argv);
  __isoc99_scanf("%s", &s1);
  if ( !strcmp(&s1, s2) )
    puts("Success");
  else
    puts("Fail!");
  return 0;
}

som vi kan se så har ida "oversatt" det hele ganske så bra.