엄범


아이디어 자체는 어렵지 않았으나 `` libc``가 아닌 다른 library의 함수가 호출될거라는 생각을 못했기 때문에 꽤 헤맸다.

recv buffer 초기화에 별로 신경을 안썼더니, send의 결과로 받은 값에 이전 send 결과로 받은 데이터가 포함되어 몇 byte 씩 틀어져 있거나 처음에 303031이 들어가 시간을 꽤 소모함.


fork를 사용하는 standalone 방식의 서버 프로그램이다.


leak

``c __isoc99_sscanf()`` 실행 이후 stack context

```

0000| 0xffffd050 --> 0xffffd070 ("1/1\n")

0004| 0xffffd054 --> 0x8049877 ("%f/%f")

0008| 0xffffd058 --> 0xffffd274 --> 0x3f800000 

0012| 0xffffd05c --> 0xffffd270 --> 0x3f800000 


gdb-peda$ x/16x 0xffffd270

0xffffd270:     0x3f800000      0x3f800000      0x53534150      0x45444f43

0xffffd280:     0x00000a7e      0x00000000      0x00000000      0x00000000

0xffffd290:     0x00000000      0x00000000      0x00000004      0x0804c008

0xffffd2a0:     0xf7fa7000      0x00000001      0xffffd318      0x080490d9

```

* 부동소수점이라 다르게 박힌다.

검사를 통과하기 위해서는 1. passcode를 leak하거나, 2. passcode에 저장되어있는 값을 null byte 등으로 변경해야 한다.

passcode에 있는 값을 변경하려면 그 위에 위치한 buf, longtitude, latitude에서 overflow가 발생해야 하는데 별 다른 overflow 지점이 보이지는 않는다.

* latitude나 longtitude의 타입이 다르기는 하지만, 어차피 ``c %f``로 읽어오기 때문에 4byte가 넘어가지는 않는다.


latitude, longtitude 4byte를 모두 채워 passcode와 이어지게 하는 것은 가능한데,

passcode를 어떻게 출력할 것인가가 문제다.

```

0000| 0xffffd050 --> 0xffffd070 ("12345678/12345678\n")

0004| 0xffffd054 --> 0x8049877 ("%f/%f")

0008| 0xffffd058 --> 0xffffd274 ("Na<KPASSCODE~\n")

0012| 0xffffd05c --> 0xffffd270 ("Na<KNa<KPASSCODE~\n")


gdb-peda$ x/s 0xffffd270

0xffffd270:     "Na<KNa<KPASSCODE~\n"

```

이런 식으로 PASSCODE가 노출될 수는 있으나 latitude나 longtitude를 대상으로 하는 출력 코드가 따로 없다.

그러나, 아무 command나 입력하면 `` [!] Unknown command : asdfds``가 발생하면서 ``c buf``가 출력되므로 `` buf, latitude, longtitude, passcode``를 모두 잇는다면 passcode가 leak된다.


exploit

launch 메뉴에서 passcode를 입력하면 thread(t1)을 생성하며, t1이 다시 t2를 생성한다.

t1은 100초가 지나면 종료되고 t2는 그냥 buf에서 `` 0x512``만큼 recv한다.

buf의 크기는 `` 0x512``가 아니라 그냥 `` 512(0x200)``이기 때문에 여기서 overflow가 발생한다.

처음 t1, t2가 생성되고 나서는 buf가 비어있기 때문에, t2는 일단 recv에서 blocking 되어 있는 상태라 데이터를 전송해 overflow를 일으킬 수 있다.


두 가지 방법으로 해결할 수 있는데, 둘 모두 PIE가 안걸려 있기 때문에 가능한 방법이다.

#1-1 libpthread RTL - system + nc

PIE가 안걸려 있으므로 ``c send(GOT)``로 libc_base를 구해 ``c system()`` 등의 주소를 알아낼 수 있으며 ret과 param위치를 조작할 수 있으니 RTL을 사용할 수 있다.


`` send@got / recv@got``를 leak해서 libc_base를 구하려고 했는데, 자꾸 안되길래 확인해보니

```bash

0xf7faf840  0xf7fbc467  Yes (*)     /lib/i386-linux-gnu/libpthread.so.0

0xf7e13490  0xf7f4499e  Yes (*)     /lib/i386-linux-gnu/libc.so.6

```

`` libpthread.so.0``도 매핑되어 있다.

```bash

gdb-peda$ print recv

$2 = {<text variable, no debug info>} 0xf7fb90a0 <recv>

```

``c recv()`` `` libc.so.6``이 아니라, ``libpthread.so.0``에 위치한다. Ubuntu 14.04.5 LTS )

추가로 ``c send() / system()``도 ``libpthread.so.0``에 위치한다. 

* 그래서 ``c system()``을 사용한 exploit은 ``c send(), recv()``를 기준으로 구한 libpt_base를 사용했을 때 제대로 동작한다.


해서, 이를 이용해 base를 구한다면 이는 `` libpt_base``가 된다.

문제는 libpthread-db같은게 존재하지 않아서 서버에서 사용하는 libpthread 버전을 알아내는 것이 어렵고, 따라서 base를 구하기가 어렵다는 점이다.


그냥 서버에 있는 libpthread를 대상으로 `` objdump``로 base와 offset을 계산했다.

https://github.com/umbum/pwn/blob/master/exploit/cg_nuclear.py#L21-L67


``c system()``을 사용하면 ``c recv()``로 ``bash /bin/sh | nc``를 넘긴 다음 이를 인자로 사용할 수 있기 때문에, stdio를 연결해 줄 필요가 없어 수월하다.


#1-2 libc RTL - dup2 + exec*

``c system()``은 libpt에 있기 때문에 사용할 수 없다. 따라서 libc에 있는 ``c exec*()``를 사용해야 한다.

xinetd가 아니라 standalone이기 때문에 표준입출력이 소켓과 연결되어있지 않아 `` nc``를 사용하는 것이 편한데,

``c exec*('/bin/sh')``를 사용한다면 `` nc``를 사용할 수 없어 `` STDIO``와 `` sockfd``를 연결해주어야 한다.


libc_base는 ``c send(), recv()``말고 다른 함수를 기준으로 구해야 한다.

여기서는 fclose@got를 사용했고 libc-database를 사용했다.

one shot을 쓰는게 간단한데, 모두 동작하지 않아 그냥 RTL했다.


https://github.com/umbum/pwn/blob/master/exploit/cg_nuclear.py#L72-L115


#2 Shellcode

``c recv()``를 사용할 수 있으니, ROP chain을 구성해 recv로 buf 위치를 지정해 적당한 곳에 쉘코드를 쓴 다음 buf로 리턴할 수 있다. 근데 ``c mprotect()``도 호출해줘야 해서 좀 번거롭다. 귀찮아서 이렇게는 안풀었다.


?

```c

snprintf(&s, 0x80u, "%c[2J%c[0;0H", 27, 27);   // shell output clear

snprintf(&s, 0x80u, "%c[%d;%dH", 27, 5, 5);    // shell cursor return to top

```