---
# System prepended metadata

title: 攻擊與防禦 HW3 Q1
tags: [攻擊與防禦]

---

# 攻擊與防禦 HW3 Q1
###### tags: `攻擊與防禦`

## Ver. Simple

```python=
import angr 

p = angr.Project("./stack1")
initial_state = p.factory.entry_state()
sm = p.factory.simulation_manager(initial_state, save_unconstrained=True)

while sm.active:
    sm.step()
if sm.unconstrained:
    for un in sm.unconstrained: # 1-1
        print("stdout:\n", un.posix.dumps(1))
        print("stdin:\n", un.posix.dumps(0), "\n")
```

### 1. Describe the overall code structure of the script.

```python=1
import angr 
```

import angr module ，此 module 為操作 angr 的 API 。

```python=3
p = angr.Project("./stack1")
initial_state = p.factory.entry_state()
sm = p.factory.simulation_manager(initial_state, save_unconstrained=True)
```

取得一個 simulation manager ，令其初始狀態為 stack1 的 entry point（即變數 `initial_state`）。

設定 `save_unconstrained` ，這樣的話會崩壞的 state 才會被存在 `uncinstrained` 中。

```python=7
while sm.active:
    sm.step()
```

持續探索 state 。

`active` 是一個存放當前狀態的陣列，若該陣列非空，函數 step 會枚舉 `active` 的所有子節點並更新到 `active` 。

```python=9
if sm.unconstrained:
    for un in sm.unconstrained: # 1-1
        print("stdout:\n", un.posix.dumps(1))
        print("stdin:\n", un.posix.dumps(0), "\n")
```

若存在被搜索到的 unconstrained 的 state ，則印出之。

官方 document 中提到 uncinstrained 可能指 

> with the instruction pointer controlled by user data or some other source of symbolic data

因此當 buffer overflow 動到 PC 時，就會發生 uncinstrained 。

### 2. Explain the purpose of the code on the lines marked with comment symbol.

#### #1-1

如 1. 所言，設定 `save_unconstrained` ，這樣的話會發生 buffer overflow 的 state 才會被存在 `uncinstrained` 中。

### 3. Test the exploit(input) on the C program and show your results.

執行腳本：

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

將此 input 餵給 stack1。

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

顯然是因為很長的 input 造成 buffer overflow。這樣的狀態因為有設定 `save_unconstrained` 的關係所以會存到 `unconstrained` 陣列中。

## Ver. Full

### 1. Describe the overall code structure of the script.

#### main part

```python=73
if __name__ == '__main__':
    filename="stack1"
    p = angr.Project(filename,auto_load_libs=False)
    state=p.factory.entry_state()
    state.globals['rbp_list']={}
    simgr = p.factory.simulation_manager(state,save_unconstrained=True)

    while simgr.active:
        for act in simgr.active:
            check_head(act)
            check_end(act)
        simgr.step()
```

上述程式碼試圖分析 stack1 ，遍歷其所有經過的 state 並以函數 check_head 和 check_end 分析之。

#### 函數 check_head

```python=57
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
    ins0=insns[0].insn
    ins1=insns[1].insn
    if len(ins0.operands)==1 and len(ins1.operands)==2:
        ins0_name=ins0.mnemonic
        ins0_op0=ins0.reg_name(ins0.operands[0].reg)
        ins1_name=ins1.mnemonic
        ins1_op0=ins1.reg_name(ins1.operands[0].reg)
        ins1_op1=ins1.reg_name(ins1.operands[1].reg)

        if ins0_name=="push" and ins0_op0=="rbp" and ins1_name=="mov" and ins1_op0=="rbp" and ins1_op1=="rsp": # 1-2
            pre_target=state.callstack.ret_addr
            state.globals['rbp_list'][hex(pre_target)]=state.regs.rbp
```

上方 code 先是分析 state 的頭兩個 instrcution ，若其為

```asm
PUSH   rbp
MOV    rbp, rsp
```

則通常代表即將進入一個函數，此時記錄一個以返回地址與 rbp 值的 key-value pair 。

#### 函數 check_end

```python=25
if state.addr==0:
    return
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
    flag=0
    for ins in insns:
        if ins.insn.mnemonic=="leave":
            flag+=1
        if ins.insn.mnemonic=="ret":
            flag+=1
    if flag==2: # 1-3
```

上方 code 先是檢查當前 state 是否含有 `leave` 和 `ret` op code 。這通常代表函數的結束。

```python=36
        rsp=state.regs.rsp
        rbp=state.regs.rbp
        byte_s=state.arch.bytes
        stack_rbp=state.memory.load(rbp,endness=angr.archinfo.Endness.LE)
        stack_ret=state.memory.load(rbp+byte_s,endness=angr.archinfo.Endness.LE)
        pre_target=state.callstack.ret_addr
        pre_rbp=state.globals['rbp_list'][hex(pre_target)]
```

上方 code 取得該函數即將進入前和即將離開前的 rbp 值和返回地址值。

```python=44
    if stack_ret.symbolic: # 1-4
        num=check_symbolic_bits(state,stack_ret)
        print_pc_overflow_msg(state,num//byte_s)
        state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
        state.memory.store(rbp+byte_s, state.solver.BVV(pre_target, 64),endness=angr.archinfo.Endness.LE)
        return
```

上方 code 比對前後返回值。若當前返回值被標記 symbolic ，表示此值來自 input ，乃是因為 buffer overflow ，然後輸出訊息。

```python=51
    if stack_rbp.symbolic: # 1-5
        num=check_symbolic_bits(state,stack_rbp)
        print_bp_overflow_msg(state,num//byte_s)
        state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
```

上方 code 比對前後 rbp 值。若當前返回值被標記 symbolic ，表示此值來自 input ，乃是因為 buffer overflow ，然後輸出訊息。

因為覆蓋返回地址比覆蓋 rbp 值要來得困難（前者需要較長的溢位），故優先檢測前者。

### 2. Explain the purpose of the code on the lines marked with comment symbol.

#### #1-2

如 1. 所言，用以判斷是否即將進入新的函數。若是，記錄其 rbp 值和預期返回地址。

#### #1-3

如 1. 所言，判斷是否含 `leave` 和 `ret` 表即將離開函數。

#### #1-4

如 1. 所言，判斷返回地址是否被修改過。若有，印出訊息。

#### #1-5

如 1. 所言，判斷 rbp 是否被修改過。若有，印出訊息。

### 3. Test the exploit(input) on the C program and show your results.

執行腳本：

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

測試造成第一個 overflow 的 input：

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

測試造成第二個 overflow 的 input：

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

兩者都造成了 buffer overflow 。
