# 攻擊與防禦 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 。