Layer7

Layer7 - 리버싱 4차시 과제

Layer7 Sechack 2022. 10. 4. 21:25
 

IDA를 쓰면 10초컷인 문제들이긴 한데 그럼 너무 날먹이니까 IDA안쓰고 gdb만으로 분석했다. (사실 수업때 gdb로 동적분석하는거만 알려주길래 IDA를 쓰면 안될것같았다.)

 

prob1

 

 

까자마자 바로 보인다. scanf에서 rbp-0xc위치에 입력 받고 그게 0xdeadbeef와 같은지 비교해준다. 다를경우엔 jne로 main+93으로 뛰고 같을경우엔 안뛴다. 안뛸 경우에는 flag_generator함수가 호출되는데 이 함수의 return값을 puts로 그대로 출력해주는거 보니까 얘가 플래그를 만들어주는 함수임을 알 수 있다.

0xdeadbeef를 10진수로 바꾸면 3735928559가 되는데 이걸 입력해보면

 

 

플래그가 나오긴 한다. 여기서 끝내면 노잼이니까 flag_generator함수를 핸드레이 해봤다.

 

 

생각보다 분량 별거 없다. 어셈으로 이정도 분량이면 C언어로는 엄청 짧게 구현되어 있을것이다.

 

아무튼 분석을 해보면 처음에 0x21짜리 malloc을 하나 해준다. 할당받은 힙 주소를 rbp-0x8에 넣고 다시 rax로 가져와준다. 그리고 나서는

 

*rax = 0x1614581d0a160e23;
*(rax+8) = 0x8300a1d0e301a5f;
*(rax+16) = 0xe301b0e300b5f5f;
*(rax+24) = 0x12165e0d020a5a5a;
*(rax+32) = 0;

 

위와같이 값을 할당해준 후 rbp-0xc위치에 0을 넣어준다.

 

 

그리고 flag_generator+132로 뛰어주는데 뛰고 나면 rbp-0xc랑 0x1f랑 비교한다. rbp-0xc에 들어있는 값이 작거나 같을 경우 flag_generator+94로 뛰어준다. flag_generator+94에서는 rbp-0xc의 값을 rdx로 가져오고 rbp-0x8의 값을 rax로 가져온다. 그 후에 rax에 rdx를 더해주고 다시 똑같은 과정을 거쳐서 rdx에도 똑같은 값을 넣어준다. 그리고 rdx가 가리키는 주소에 있는 값을 가져와서 edx에 넣어주고 0x6f랑 xor한뒤에 rdx의 하위 1byte인 dl을 rax가 가리키는 주소에 넣어준다. 그 후에 rbp-0xc에 들어있는 값을 1증가시키고 다시 0x1f랑 비교한다. 즉 0x1f만큼 루틴을 반복한다는걸 알 수 있다. 전형적인 for문의 형태이다. rbp-0xc의 값은 for문에서 사용되는 카운트 변수라는것을 알 수 있고 rbp-0x8에 들어간건 할당받은 힙주소니까 아까 할당한 값에서 인덱스를 하나하나 늘려가면서 0x6f랑 xor하면 플래그가 나온다는 사실을 알 수 있다.

 

#include <stdio.h>
#include <stdlib.h>

char *flag_generator(){
    long long *s = malloc(0x21);
    s[0] = 0x1614581d0a160e23;
    s[1] = 0x8300a1d0e301a5f;
    s[2] = 0xe301b0e300b5f5f;
    s[3] = 0x12165e0d020a5a5a;
    s[4] = 0;
    for(int i = 0; i <= 0x1f; i++)
    	*((char *)s+i) ^= 0x6f;
    return s;
}

int main(void){
    puts(flag_generator());

    return 0;
}

 

C언어로 핸드레이 해보면 이렇게 짤 수 있고

 

 

잘 나온다. python으로도 짜보면

 

import struct

s = [0 for i in range(0x21)]
s[0] = struct.pack('<Q', 0x1614581d0a160e23)
s[8] = struct.pack('<Q', 0x8300a1d0e301a5f)
s[16] = struct.pack('<Q', 0xe301b0e300b5f5f)
s[24] = struct.pack('<Q', 0x12165e0d020a5a5a)
s[32] = struct.pack('<Q', 0)
s = b''.join([s[i] for i in range(0, 33, 8)])

for i in range(0x20):
    print(chr(s[i]^0x6f), end='')

 

 

잘 나온다.

 

 

prob2

 

 

   0x000000000040075b <+0>:     push   rbp
   0x000000000040075c <+1>:     mov    rbp,rsp
   0x000000000040075f <+4>:     sub    rsp,0x20
   0x0000000000400763 <+8>:     mov    rax,QWORD PTR fs:0x28
   0x000000000040076c <+17>:    mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400770 <+21>:    xor    eax,eax
   0x0000000000400772 <+23>:    mov    edi,0xfeedc0de
   0x0000000000400777 <+28>:    call   0x400580 <srand@plt>
   0x000000000040077c <+33>:    mov    DWORD PTR [rbp-0x10],0x0
   0x0000000000400783 <+40>:    jmp    0x4007ca <main+111>
   0x0000000000400785 <+42>:    mov    edi,0x4008d4
   0x000000000040078a <+47>:    call   0x400550 <puts@plt>
   0x000000000040078f <+52>:    call   0x4005b0 <rand@plt>
   0x0000000000400794 <+57>:    mov    DWORD PTR [rbp-0xc],eax
   0x0000000000400797 <+60>:    lea    rax,[rbp-0x14]
   0x000000000040079b <+64>:    mov    rsi,rax
   0x000000000040079e <+67>:    mov    edi,0x4008ec
   0x00000000004007a3 <+72>:    mov    eax,0x0
   0x00000000004007a8 <+77>:    call   0x4005a0 <__isoc99_scanf@plt>
   0x00000000004007ad <+82>:    mov    eax,DWORD PTR [rbp-0x14]
   0x00000000004007b0 <+85>:    cmp    DWORD PTR [rbp-0xc],eax
   0x00000000004007b3 <+88>:    je     0x4007c6 <main+107>
   0x00000000004007b5 <+90>:    mov    edi,0x4008ef
   0x00000000004007ba <+95>:    call   0x400550 <puts@plt>
   0x00000000004007bf <+100>:   mov    eax,0x1
   0x00000000004007c4 <+105>:   jmp    0x40082c <main+209>
   0x00000000004007c6 <+107>:   add    DWORD PTR [rbp-0x10],0x1
   0x00000000004007ca <+111>:   cmp    DWORD PTR [rbp-0x10],0x13
   0x00000000004007ce <+115>:   jle    0x400785 <main+42>
   0x00000000004007d0 <+117>:   mov    edi,0x4008f8
   0x00000000004007d5 <+122>:   call   0x400550 <puts@plt>
   0x00000000004007da <+127>:   lea    rax,[rbp-0x14]
   0x00000000004007de <+131>:   mov    rsi,rax
   0x00000000004007e1 <+134>:   mov    edi,0x4008ec
   0x00000000004007e6 <+139>:   mov    eax,0x0
   0x00000000004007eb <+144>:   call   0x4005a0 <__isoc99_scanf@plt>
   0x00000000004007f0 <+149>:   mov    eax,DWORD PTR [rbp-0x14]
   0x00000000004007f3 <+152>:   cmp    eax,0xdeadface
   0x00000000004007f8 <+157>:   jne    0x40081d <main+194>
   0x00000000004007fa <+159>:   mov    edi,0x40090d
   0x00000000004007ff <+164>:   call   0x400550 <puts@plt>
   0x0000000000400804 <+169>:   mov    eax,0x0
   0x0000000000400809 <+174>:   call   0x4006c6 <flag_generator>
   0x000000000040080e <+179>:   mov    rdi,rax
   0x0000000000400811 <+182>:   call   0x400550 <puts@plt>
   0x0000000000400816 <+187>:   mov    eax,0x0
   0x000000000040081b <+192>:   jmp    0x40082c <main+209>
   0x000000000040081d <+194>:   mov    edi,0x400916
   0x0000000000400822 <+199>:   call   0x400550 <puts@plt>
   0x0000000000400827 <+204>:   mov    eax,0x1
   0x000000000040082c <+209>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000400830 <+213>:   xor    rdx,QWORD PTR fs:0x28
   0x0000000000400839 <+222>:   je     0x400840 <main+229>
   0x000000000040083b <+224>:   call   0x400560 <__stack_chk_fail@plt>
   0x0000000000400840 <+229>:   leave
   0x0000000000400841 <+230>:   ret

 

얘는 아까 문제보다 살짝 길어졌다. flag_generator은 보니까 아까 루틴이랑 똑같고 xor하는 값만 바꼈기 때문에 딱히 핸드레이 할 필요가 없다. 이번에는 플래그 주는 조건만 분석하겠다.

 

먼저 scand함수로 0xfeedc0de를 인자로 넘겨준다. 10진수로 바꾸면 4276994270이다.

 

   0x000000000040078f <+52>:    call   0x4005b0 <rand@plt>
   0x0000000000400794 <+57>:    mov    DWORD PTR [rbp-0xc],eax
   0x0000000000400797 <+60>:    lea    rax,[rbp-0x14]
   0x000000000040079b <+64>:    mov    rsi,rax
   0x000000000040079e <+67>:    mov    edi,0x4008ec
   0x00000000004007a3 <+72>:    mov    eax,0x0
   0x00000000004007a8 <+77>:    call   0x4005a0 <__isoc99_scanf@plt>
   0x00000000004007ad <+82>:    mov    eax,DWORD PTR [rbp-0x14]
   0x00000000004007b0 <+85>:    cmp    DWORD PTR [rbp-0xc],eax
   0x00000000004007b3 <+88>:    je     0x4007c6 <main+107>
   0x00000000004007b5 <+90>:    mov    edi,0x4008ef
   0x00000000004007ba <+95>:    call   0x400550 <puts@plt>
   0x00000000004007bf <+100>:   mov    eax,0x1
   0x00000000004007c4 <+105>:   jmp    0x40082c <main+209>
   0x00000000004007c6 <+107>:   add    DWORD PTR [rbp-0x10],0x1
   0x00000000004007ca <+111>:   cmp    DWORD PTR [rbp-0x10],0x13
   0x00000000004007ce <+115>:   jle    0x400785 <main+42>

 

그다음 rand부분 부터 보면 아까 flag_generator를 분석했을때 봤던 형태와 유사하다. rbp-0x10이 카운트 변수이고 반복문임을 알 수 있다. scanf로 입력을 받고 입력받은 값이 rand함수로 생성한 값과 같은지 체크한다. 이 과정을 20번 거친다.

 

 

20번 모두 맞으면 마지막으로 입력을 한번 더 받아서 0xdeadface인지 체크하고 맞을 경우에는 flag_generator를 호출하는것을 볼 수 있다.

 

rand함수는 seed값을 기반으로 내부적으로 복잡한 비트 연산을 거쳐서 랜덤처럼 보이는 값을 생성하는걸로 알고 있다. 즉 seed값이 같으면 수행하는 연산이 항상 같으므로 같은 값이 나온다는 말이다. 그래서 보통 time과 같이 계속해서 변하는 값을 seed로 주는데 그래도 예측하기 쉽지만 위와 같이 seed값을 고정시킬 경우에는 사실상 랜덤이 아니게 된다.

 

from ctypes import *

libc=CDLL("/lib/x86_64-linux-gnu/libc.so.6")

libc.srand(0xfeedc0de)

for i in range(20):
    print(libc.rand())

print(0xdeadface)

 

python ctypes를 이용해서 seed를 똑같이 주고 C언어의 rand를 부르면 값이 똑같이 나온다.

 

 

이렇게 나온 값을 복붙하면

 

root@fc0ce980d0ed ~/hacking
❯ ./prob2
guessing random number!
1298921064
602727545
1936120676
10262152
1169740271
1286574116
1898974427
1351434765
1657355885
1589073855
1596799434
937238354
879507685
1388661204
618576540
649908474
16549218
2040108771
1813507610
468945779
3735943886guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
guessing random number!
input correct number

correct!
Layer7{Y0u_are_m45ter_0f_A55EMB1Y!}

 

플래그가 나온다.