일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- RITSEC CTF 2019
- BOF
- buffer over flow
- x64dbg
- webhacking
- 보안뉴스
- 뉴스클리핑
- Python
- Hackerschool
- Next.js
- SQL Injection
- 리눅스
- termux
- Linux
- NewsClipping
- PWN
- requests
- ctf
- writeup
- 버퍼오버플로우
- RITSEC
- 어셈블리어
- Nop Slide
- CodeEngn
- ftz
- 리버싱
- 웹해킹
- reversing
- HackCTF
- Shadow 동아리
- Today
- Total
Jaeseo's Information Security Story
Codeengn Basic RCE 14번 문제 본문
Basic RCE L14
Name이 CodeEngn 일때 Serial을 구하시오
(이 문제는 정답이 여러개 나올 수 있는 문제이며 5개의 숫자로 되어있는 정답을 찾아야함, bruteforce 필요) Ex) 11111
일단 문제를 풀기 전에 PE 분석 프로그램을 사용합니다.
upx 패킹이 되어 있는 모습을 볼 수 있는데 upx -d 명령어를 통해 언패킹 후 분석을 합니다!
일단 최근 공부를 하면서 IDA에 대해 알게 되었는데 분석하는데 진짜 도움이 많이 되서 한번 IDA로 정적분석을 해봅니다.
일단 main 함수 입니다.
void __stdcall __noreturn start(int a1)
{
hInstance = GetModuleHandleA(0); //응용 프로그램의 인스턴스 핸들을 받아서 hInstance에 저장
sub_401025(hInstance); //hInstance를 인자로 sub_401025() 실행
ExitProcess(0); //종료
}
일단 sub_401025()함수를 실행 하는 모습을 볼 수 있는데 분석을 합니다.
WPARAM __stdcall sub_401025(HINSTANCE hInstance)
{
HWND v1; // eax
HWND v2; // eax
HWND v3; // eax
HWND v4; // eax
HWND hDlg; // [esp+0h] [ebp-50h]
struct tagMSG Msg; // [esp+4h] [ebp-4Ch]
WNDCLASSEXA v8; // [esp+20h] [ebp-30h] // 파워빌더? 구조체 선언
LoadIconA(::hInstance, (LPCSTR)0x64);
v8.cbSize = 48;
v8.style = 3;
v8.lpfnWndProc = sub_4011E2; //메세지 처리 함수 지정
v8.cbClsExtra = 0;
v8.cbWndExtra = 30;
v8.hInstance = hInstance;
v8.hbrBackground = (HBRUSH)5;
v8.lpszMenuName = 0;
v8.lpszClassName = aBengaly;
v8.hIcon = LoadIconA(::hInstance, (LPCSTR)0x64);
v8.hIconSm = v8.hIcon;
v8.hCursor = LoadCursorA(0, (LPCSTR)0x7F00);
RegisterClassExA(&v8);
hDlg = CreateDialogParamA(::hInstance, TemplateName, 0, 0, 0);
ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0xC8);
v1 = GetDlgItem(hDlg, 108);
SendMessageA(v1, 0xF7u, 0, ho);
ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0x12C);
v2 = GetDlgItem(hDlg, 112);
SendMessageA(v2, 0xF7u, 0, ho);
ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0x190);
v3 = GetDlgItem(hDlg, 109);
SendMessageA(v3, 0xF7u, 0, ho);
ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0x1F4);
v4 = GetDlgItem(hDlg, 113);
SendMessageA(v4, 0xF7u, 0, ho);
SetWindowTextA(hDlg, String);
UpdateWindow(hDlg);
while ( GetMessageA(&Msg, 0, 0, 0) )
{
if ( !IsDialogMessageA(hDlg, &Msg) )
{
TranslateMessage(&Msg);
DispatchMessageA(&Msg);
DeleteObject((HGDIOBJ)ho);
DeleteObject((HGDIOBJ)ho);
}
}
return Msg.wParam;
}
소스를 보면 일단 파워빌더를 통해 모양을 잡고 윈도우를 생성하는 것으로 추측이 됩니다. 이때 lpfnWndProc에 sub_4011E2 함수를넣는 모습을 볼 수 있는데 이때는 메세지 처리 함수를 지정하는 것으로 아래와 같이 버튼을 누르고 등의 처리를 담당하는 것으로 예상이 됩니다.
이제 처리를 담당하는 sub_4011E2에 들어가 분석을 합니다.
int __stdcall sub_4011E2(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
int v5; // esi
int v6; // ecx
signed int v7; // eax
if ( Msg == 2 ) //WM_DESTROY := 0x02
{
PostQuitMessage(0);
}
else if ( Msg == 513 ) //WM_LBUTTONDOWN := 0x201
{
ReleaseCapture();
SendMessageA(hWnd, 0xA1u, 2u, 0);
}
//WM_COMMAND := 0x111 == 273 메뉴가 클릭 되었을 때 발생 wParam,lParam를 통해 어떤 메뉴인지에 대해 판단
else if ( Msg == 273 && !(wParam >> 16) && wParam == 109 )
//종료에 대한 메뉴
{
ExitProcess(0);
}
if ( Msg == 273 && !(wParam >> 16) && wParam == 113 )
//최소화 처리에 대한 메뉴
ShowWindow(hWnd, 6);
if ( Msg != 273 )
//윈도우에서 기본 처리
return DefWindowProcA(hWnd, Msg, wParam, lParam);
if ( !(wParam >> 16) && wParam == 112 )
//ABOUT에 관한 메세지 출력
MessageBoxA(0, Text, Caption, 0);
/*
Text =
+=================================+
|Key/CrackMe - 2 Created on 21/9/2001|
+=================================+
....이하생략....
Caption = Key/CrackMe #2
*/
if ( !(wParam >> 16) && wParam == 108 )
//Check 버튼이 입력 될때 처리
{
if ( !GetDlgItemTextA(hWnd, 106, dword_403038, 64) || !GetDlgItemTextA(hWnd, 107, byte_403138, 64) )
//둘중 하나라도 입력값이 없어 0이 리턴 되면 메세지 박스 출력
return MessageBoxA(0, aPleaseFillIn1M, Caption, 0);
/*
aPleaseFillIn1M = Please Fill in 1 more Char!!
Caption = Key/CrackMe #2
*/
v5 = 0;
v6 = lstrlenA(dword_403038);
//id의 길이를 저장시킴
v7 = 1;
/*
dword_403038 == id 변수
byte_403138 == password 변수
아래의 설명 이미지 참조!
*/
do
{
v5 = ((signed int)(unsigned __int8)byte_403037[v7] >> 1)
//id의 v7의 인덱스에 들어가 >> 1 연산 즉 1/2
+ (unsigned __int8)byte_403037[v7] * (unsigned __int8)byte_403037[v7]
//위의 결과에 + id의 v7의 인덱스의 값을 제곱
+ v5
//기존의 v5 추가
- (unsigned __int8)byte_403037[v7];
// - id의 v7의 인덱스의 값을 뺀다.
++v7;
//인덱스를 위한 변수 1증가
--v6;
//반복 변수 1--
}
while ( v6 );
//아이디의 길이를 반복할때 마다 v6-- 반복
if ( sub_401383(byte_403138) == v5 )
//만약 password가 v5와 같으면 성공 메세지 출력
MessageBoxA(0, aGoodJobIWishYo, Caption, 0);
else
//아니라면 실패 메세지 출력
MessageBoxA(0, aYouHaveEnterAW, Caption, 0);
}
return 0;
}
참조 이미지
위와 같이 중간에 x64dbg로 동적으로 어디에 사용하는지에 대해 분석도 하면서 IDA를 주로 사용 하며 파악을 하였는데 이 프로그램은 ID를 기준으로 특수한 알고리즘 연산을 통해 Password를 뽑아내는 것으로 보입니다. 이점을 이용하여 CodeEngn일때의 Password를 알아내봅니다.
직접 X64dbg를 통해서 ID가 CodeEngn일때의 알고리즘 후 비교하는 레지스터값을 분석 하는 방법도 있지만 한번 IDA에서 가져온 소스를 조금 수정 하여 출력 해봤습니다.
#include <stdio.h>
int main(int argc, char const *argv[])
{
int v5 = 0;
char *id = "CodeEngn";
int v6 = 8;
//id의 길이를 저장시킴
signed int v7 = 0;
/*
dword_403038 == id 변수
byte_403138 == password 변수
아래의 설명 이미지 참조!
*/
do
{
v5 = ((signed int)(unsigned __int8)id[v7] >> 1)
//id의 v7의 인덱스에 들어가 >> 1 연산 즉 1/2
+ (unsigned __int8)id[v7] * (unsigned __int8)id[v7]
//위의 결과에 + id의 v7의 인덱스의 값을 제곱
+ v5
//기존의 v5 추가
- (unsigned __int8)id[v7];
// - id의 v7의 인덱스의 값을 뺀다.
++v7;
//인덱스를 위한 변수 1증가
--v6;
//반복 변수 1--
}
while ( v6 );
printf("%d",v5);
return 0;
}
그 결과 아래의 결과가 나오는 것을 확인할 수 있는데 직접 테스트를 해봅니다.
[Running] cd "d:\OneDrive - JaeSeo\Codeengn\" && gcc 14.c -o 14 && "d:\OneDrive - JaeSeo\Codeengn\"14
76193
[Done] exited with code=0 in 0.335 seconds
AUTH:76193
13번까지 예전에 풀어두고 이제서야 블로그에 올린 뒤 14번을 풀어서 올리네요...
'Write UP > Codeengn - basic' 카테고리의 다른 글
Codeengn Basic RCE 16번 문제 (0) | 2019.12.03 |
---|---|
Codeengn Basic RCE 15번 문제 (0) | 2019.12.01 |
Codeengn Basic RCE 13번 문제 (0) | 2019.11.30 |
Codeengn Basic RCE 12번 문제 (0) | 2019.11.30 |
Codeengn Basic RCE 11번 문제 (0) | 2019.11.30 |