plt로 got 채우기

puts@plt + 0에 puts@plt + 6의 주소가 들어있어서 호출되면 puts@plt+6의 코드가실행

puts@plt + 11 에서 점프하면 pushl, jmp로 분기된다.
pushl로 GOT+4의 주소를 스택에 넣고 GOT+8의 주소로 분기하는데 여기에는_dl_runtime_resolve의 주소가 있다.
_dl_runtime_resolve 에서 _dl_fixup 을 호출한다.

_dl_fixup 함수에서는 edi에 puts의 offset을 넣고 esi에 .strtab의 주소를 넣는다

(.strtab + offset = 'puts' 문자열이 들어가있다.)
eax에는 'puts'문자열의 주소를 넣고 _dl_lookup_symbol_x 함수를 호출한다.

_dl_lookup_symbol_x 함수에서는 SYMTAB의 주소와 lib의 주소를 얻고 _dl_fixup 함수로 나와서 SYMTAB에서 아까전에 넣어논 esi(.strtab), edi(offset)응 이용해 실제 주소를 구하고 GOT에 write하고 _dl_runtime_resolve 함수로 다시와서 puts함수로 넘어가게 된다.

이런식으로 한번 dynamic linker가 주소를 받아오는 과정을 거치게 되면 다음번에는_dl_runtime_resolve함수로 들어가지 않고 바로 puts@plt에서 바로 실제 puts함수의 코드로 들어가게 된다.
--------------------------------------------------------------------------------------------
1. puts@plt
2. puts@plt+6
3. puts@plt+11
4. _dl_runtime_resolve(GOT+8)
 실행 전에는 GOT+8이 0임, 메모리에 올라가면 GOT+8에 _dl_runtime_resolve의 주소가 들어감
5. _dl_fixup
 edi : puts의 offset
 esi : &.strlab
 eax : &"puts"
 <이놈들을 조작할 수 있으면 원하는 함수를 실행 할 수 있다.>
6. _dl_lookup_symbol_x 
 SYMTAB주소 얻음
 lib의 주소 얻음
7. _dl_fixup
 edi : puts의 offset
 esi : &.strlab
 이 두개를 이용해 SYMTAB에서 실제 주소 구해서 GOT에 write
8. _dl_runtime_resolve
 puts의 공유라이브러리(실제 puts함수)로 넘어감
--------------------------------------------------------------------------------------------
Read more

포멧 스트링 버그, Format String Bug(2)

포멧 스트링 버그, Format String Bug

저번 포스팅에서 포멧 스트링 버그를 이용해서 메모리 릭을 하는 방법과 %n 포멧스트링을 어떻게 사용하는지 까지 설명하였다. 이번 포스팅에서는 직접 원하는 메모리에 %n 포멧스트링을 이용하여 원하는 값을 넣어보자.

먼저 확인할 코드!



메모리가 제대로 조작되는지 확인하기 위해 저번 포스팅의 코드에서 dumpcode만 추가 하였다.
dumpcode함수를 사용하기 위해서는 dumpcode.h파일이 있어야한다. 



위의 포스팅으로 들어가서 코드를 복사한뒤 dumpcode.h파일을 만들어 붙여넣기 하면 된다.



버퍼부터 시작하여 100bytes 만큼의 메모리를 출력해준다. 이제 %n을 이용해서 아무 메모리에나 값을 써보자.
0xffffd6d8에 값을 써보기로 한다.



0xffffd6d8에 출력된 문자열의 길이인 0xe값이 들어갔다 성공이다! 근데 메모리를 변조하여 공부하려면 최소한 4bytes는 변조를 해야하는데 한번에 0~65535, 0x00~0xFF, 2byte 밖에 수정하지 못한다. 그래서 두번에 나누어 주소값을 써줘야 한다.
처음번에 0xffffd6d8에 값을 쓰고 두번째에는 0xffffd6da에 나머지 값을 써야한다. exploit을 할때는 당연히 엔디언을 생각해서 제대로된 값이 들어가도록 해야한다.
지금은 테스트이고 귀찮으니까 그냥 0xffffd6d8에 0xffffffff 값을 써넣어 보자. 



엄청난 공백이 생기고 0xffffd6d8에 0xffffffff의 값이 써진것을 확인할 수 있다.

python -c 'print "\xd8\xd6\xff\xffAAAA\xda\xd6\xff\xff%65521x%x%n%65536x%n"' | ./fsb1

쉘코드를 보면 \xd8\xd6\xff\xff와 뒤쪽의 첫번째 %n과 매치가 된다. 여기에 들어가는 값을 조정하기 위해 %n앞에 %x%x 이 두개를 이용해 메모리에 쓸 값을 맞출 수 있다. 그리고 다음 2bytes를 덮기 위해서는 \xda\xd6\xff\xff을 %n과 매치시켜주면 되지만 바로 매치시키면 메모리에 쓸 데이터 조절을 할수가 없다. 그래서 임의로 의미없는 데이터인 AAAA를 %x로 출력해주고 이 부분에서 뒷부분 2bytes에 들어갈 값을 조절할 수 있게 된다.

이제 fsb가 발생하면 원하는 주소의 메모리값을 바꿀수 있게 되었다. 이를 이용해 ret, got, dtor등을 조작해 exploit에 성공할 수 있다.
Read more

포멧 스트링 버그, Format String Bug(1)

포멧 스트링 버그, Format String Bug

포멧 스트링 버그는 printf 함수 등에서 사용되는 포멧 스트링(%d, %s...)을 잘못된 형태로 사용할 경우 발생하는 버그이다. 포멧 스트링은 데이터를 어떤 형식에 따라 입력받거나 출력하기 위하여 사용하는데 형식을 잘못사용하면 메모리의 데이터가 유출되고 변조될 수 있다.
포멧 스트링 버그는 메모리 릭, 메모리 변조 모두 가능하기 때문에 취약점이 발생하면 exploitable할 가능성이 높다.

printf함수는 아래의 두가지 방법으로 사용할 수 있다.

printf(BUFFER)
printf("FORMAT_STRING", BUFFER)

포멧 스트링 버그는 포멧스트링을 사용하지 않고 바로 BUFFER를 출력하는 첫번째 printf 함수 사용법에서 발생한다. 만약 BUFFER 안에 %x, %d등 포멧 스트링이 들어가면 printf함수는 그것들을 포멧스트링으로 보고 두번째 printf 함수 사용법과 같이 동작하여 스택 메모리를 유출시킨다.

예제 코드를 작성하여 첫번째 방법과 두번째 방법의 printf함수를 비교하며 동작시 스택에 쌓이는 데이터들을 확인해보자.
간단한 에코 프로그램이다. 7번째 줄의 printf에서는 포멧스트링 버그가 발생하고 9번째 줄의 printf에서는 포멧스트링 버그가 발생하지 않는다. 

아래는 포멧스트링버그가 발생하지 않는 9번째 줄의 printf 함수 호출 직전의 스택 모양과 ESP가 가리키는 곳이다.



printf 함수를 콜하게 되면 먼저 FORMAT_STRING을 POP한다. ESP는 한칸 내려가 &BUFFER를 가리킨다. 
FORMAT_STRING을 읽으면서 문자열은 그냥 출력하고 포멧스트링을 만난다면 POP을 하면서 BUFFER의 데이터를 4bytes씩 짝에 맞는 포멧스트링 형식으로 출력을 해준다.

EX)
FORMAT_STRING = "HI, %d, %d!!"
BUFFER[] = {10, 20}
BUFFER 메모리에는 0x00000014 0x0000000A.
이 데이터들을 pop하여 FORMAT_STRING과 매핑.

출력 : HI, 20, 10!!



이런식으로 문자열을 하나잡고 출력하면서 포멧스트링(%d, %c..)을 만나면 BUFFER에서 4byte씩 뽑아와서 형식에 맞게 출력을 한다.

이제 포멧스트링버그가 발생하는 9번째 줄의 printf 함수 호출 직전의 스택모양을 확인해보자.



FORMAT_STRING 문자열이 들어가 있던곳에도 BUFFER의 주소가 들어가 있다. 만약 BUFFER안에 %d같은 포멧스트링이 없다면 그냥 BUFFER를 출력하고 버그없이 printf함수가 종료될 것이다. 하지만 포멧스트링이 있다면 위의 제대로 사용된 printf함수와 같이 동작하게 된다. 
왼쪽 사진에서 ESP가 가리키는 BUFFER를 출력해 나가다가 포멧 스트링을 만나게되면 두번째 BUFFER에서 데이터를 뽑아서 형식에 맞게 출력을 해준다. 즉 하나의 문자열(문자+포멧스트링)로 출력을 하면서 데이터를 뽑아 오기까지 한다.




위의 예제 코드를 컴파일 하고 테스트를 통해 확인하여 보자.



바이너리를 실행하고 입력을 AAAA %x %x %x %x로 주었다.

위 화면의 AAAA %x %x %x %x 은 키보드입력값이고 그 다음줄에 나오는
AAAA ffffd6a8 40 41414141 20782520 은 잘못된 포멧스트링 사용으로 인해 메모리 릭이 일어난 것이고 I am safa!, AAAA %x %x %x %x는 정상적인 포멧스트링 사용으로 제대로된 문자열이 출력되었다. 

더 보자. 처음 AAAA는 문자열이니 그냥 출력이 되었고 %x를 만났다. 첫번째 %x는 위의 스택그림의 &BUFFER와 매핑되어 BUFFER의 주소가 출력되었다. ffffd6a8은 BUFFER의 주소이다. 그리고 두번째 %x는 더미같은 40과 매핑되어 40이 출력되었다 그리고 두, 세번째 %x는 BUFFER와 매핑되어 BUFFER 맨 앞에 8bytes가 출력되었다. A는 아스키코드로 0x41이고 스페이스는 0x20, %는 0x25, x는 0x78이다.

여기서 이 버그를 제대로된 취약점으로 승화 시킬수 있다.

잘보면 BUFFER의 앞의 AAAA를 뒤의 %x를 이용해서 출력을 하는 것을 볼 수 있다. 우리의 입력값을 우리가 출력하게 만들수 있게 되었다. 만약에 AAAA에 우리가 원하는 데이터의 주소를 넣는다면 AAAA주소에 있는 데이터를 릭하게 될것이다.


코드를 위와 같이 수정하고 컴파일하여 실행하여 buf1에 들어있는 DEAD문자열을 릭해보자.



BUFFER의 맨 앞부분에 DEAD문자열의 주소값을 엔디언이 맞게 넣어둔다. 그리고 %x를 이용해 BUFFER의 주소를 뽑아서 출력하고 0x40을 뽑아서 출력한다. 그리고 마지막에 %s를 이용해 조금전에 입력한 DEAD의 주소값을 뽑아와서 %s형식으로 출력해준다. DEAD 문자열이 릭되었다.

[    DEAD의 주소     ][    %x    ][  %x ][ %s  ] // 쉘코드 구성
[????(DEAD의 주소)][&BUFFER][0x40][DEAD] // 출력결과

스택을 pop하면서 포멧스트링에 맞게 출력을 하다 우리가 입력한 BUFFER까지 pop을 하여 출력을 하게된다. 그래서 BUFFER에 임의의 주소값을 넣고 %s로 출력을 하면 원하는 메모리를 릭할 수 있다.

이제 %x, %s 이외의 포멧스트링버그의 핵심인 %n에 대해 알아보자.
%n은 현재까지 출력한 문자의 개수를 메모리에 쓰는 포멧 스트링이다. 사용예제를 확인해보자.
이 코드를 실행하면 아래의 화면이 출력된다.



%n으로 &i에 출력한 개수를 쓴다. 위에서 확인했듯이 printf의 두번째 인자인 포멧스트링의 형식의 데이터가 들어있는 스택을 조작할 수 있다. AAAA에 주소를 넣고 %s로 출력시켜 원하는 메모리 주소를 릭 시켰던것 처럼 원하는 메모리주소에 %n을 이용하여 원하는 값을 넣을 수 있다.
%n 은 출력한 값들 개수를 메모리에 쓰는것인데 버퍼의 크기등의 문제로 많은 데이터를 입력하기 어렵다. 원하는 값을 맞추기 위해서는 %10d, %100c 등의 포멧스트링을 사용해야 한다.

%n을 이용해서 원하는 주소의 메모리에 데이터를 쓰는 건 다음 포스팅에서 알아보도록 하자.
힘들다.
Read more