[Swing CTF] Reversing - Randsomware
Randsomware
문제 링크: http://swingctf.hspace.io:9090/challenges#Randsomware-9
IDA Pro 프로그램을 이용할 것이다.
IDA? (처음 써보는 프로그램이라 간단하게 사용법부터 정리하겠다..)
디스어셈블러의 일종으로, 디스어셈블러는 바이너리 파일을 역으로 어셈블리어로 재구성해주는 툴이다.
인터페이스
좌: functions window, 우: 해당 함수에 대한 description
Pseudo Code 보기
변환을 원하는 함수를 선택한 후 F5를 누르면 된다. 그러면 창에 Pseudocode라는 창이 새로 생기면서 프로그래밍 변환된 걸 보여준다. 어셈블리어 코드를 다시 pseudocode로 자동적으로 바꾼 것이기 때문에 예를 들어 __int64 -> int, _isoc99_scanf -> scanf로 눈치껏 해석하는 것이 필요하다..
Write Up
랜덤값이 buf에 담기고, rand()로 무작위 시드를 뽑은 후 16바이트 키를 만든다.
unsigned __int64 init()
{
int i; // [rsp+8h] [rbp-18h]
int fd; // [rsp+Ch] [rbp-14h]
__int64 buf; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
fd = open("/dev/urandom", 0);
buf = 0LL;
read(fd, &buf, 8uLL);
close(fd);
srand(buf);
for ( i = 0; i <= 15; ++i )
key[i] = rand();
return v4 - __readfsqword(0x28u);
}
그 후 16바이트 키를 가져와서 XOR을 통해 flag.png를 암호화 한다.
정말 무작위 값이기 때문에 그 값을 유추하는 것은 불가능하다.
하지만 XOR연산에는 하나의 특성이 있다.
A ⊕ B = C
C ⊕ A = B
이 특성을 이용해서 문제를 풀어줄 것이다. png 헤더는 값이 일정하기 때문에 원래 png 헤더값과 암호화된 png 헤더값을 XOR 하면 원본 키 16바이트를 구할 수 있을 것이다.
header ⊕ key = enc
enc ⊕ header = key
png 파일의 헤더 시그니처: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 (밑줄 친 부분은 png의 헤더 시그니처)
암호화 된 png 파일의 헤더 시그니처: 3D BB CA 2D 54 C7 82 20 D4 F9 3B E9 C2 91 81 A4
우선 문제를 다운받은 prob 파일 내에 파이썬 파일(.py)을 하나 생성한다.
prob 파일 아래에 파이썬 파일을 생성하는 이유는 암호화 된 png 파일을 파이썬에서 더 쉽게 불러오기 위해서이다!
open()
: open(filename, mode)
mode
- r(read), w(write), a(append)
- 인코딩 모드
- t(text): 자동 인코딩/디코딩 모드
- read(): 반환타입 str
- write(): str으로 자동 변환
- b(binary): 바이너리 모드
- read(): 반환 타입은 bytes
- write(): bytes으로 자동 반환
ex) rt(read+text), rb, wt, wb, at, ab
png 헤더(header)와 암호화된 png파일의 헤더(enc) 읽는다.
header = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52]
enc = open("./enc_verysecret.png", 'rb').read() #파일 전체를 byte 형식으로 읽기->'rb'
암호화 된 png의 헤더 중 16바이트(0x10)를 temp 배열에 저장한다. 파일의 헤더 부분이므로 앞에서 16바이트를 저장해주면 된다. -> [:0x10]
그리고 원래 png헤더와 암호화 된 png 헤더를 XOR(^) 연산을 통해서 key 값을 구해준다.
temp = enc[:0x10] #암호화 된 png 헤더
key = []
for i in range(0x10): #키 값 구하기
key.append(header[i] ^ temp[i])
이제 위에서 구한 key를 가지고 enc(enc_verysecret.png)를 복호화를 해준다.
solve = []
for i in range(len(enc)): #암호화 된 png의 길이만큼 반복
solve.append(enc[i] ^ key[i % 0x10])
join()
join()은 매개변수로 들어온 리스트에 있는 요소 하나하나를 합쳐서 하나의 문자열로 바꾸어 변환하는 함수이다.
''.join(리스트)
['a', 'b', 'c'] -> 'abc'
'구분자'.join(리스트)
['a', 'b', 'c'] -> "a_b_c"
chr()
아스키코드를 문자열로 변환하는 함수이다. (<-> ord())
chr(아스키코드)
solve = ''.join(chr(_) for _ in solve)
open("./flag.png", 'wb').write(solve.encode('iso-8859-1'))
print("완료")
<전체 코드>
header = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52]
enc = open("./enc_verysecret.png", 'rb').read()
temp = enc[:0x10]
key = []
for i in range(0x10):
key.append(header[i] ^ temp[i])
solve = []
for i in range(len(enc)):
solve.append(enc[i] ^ key[i % 0x10])
solve = ''.join(chr(_) for _ in solve)
open("./flag.png", 'wb').write(solve.encode('iso-8859-1'))
print("완료") #함수 실행 완료 시, '완료' 출력
SWING{Secure_rand_ransomware}