首页
社区
课程
招聘
[原创]CTF-PWN常规题个人实战笔记(持续更新)
发表于: 2021-2-24 12:18 15588

[原创]CTF-PWN常规题个人实战笔记(持续更新)

2021-2-24 12:18
15588

本文主要记录一些个人在CTF PWN(主要是linux用户态)中遇到的一些坑,以及一些方法/套路的总结。

https://xuanxuanblingbling.github.io/ctf/tools/2020/03/20/gdb/

https://blog.csdn.net/gatieme/article/details/51671430

https://www.jianshu.com/p/febaae0c410d

其中 --set-rpath 后面跟题目给的 libc.so.6 路径,然后最后是文件名

效果如下

修改前:

root@ubuntu:~/roarctf# ldd 2+1
linux-vdso.so.1 => (0x00007fff2b9d9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f37286c1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3728a8b000)

修改后:

root@ubuntu:~/roarctf# ldd 2a1
linux-vdso.so.1 => (0x00007fff12b47000)
libc.so.6 => /root/roarctf/libc.so.6 (0x00007fca605af000)
/lib64/ld-linux-x86-64.so.2 (0x00007fca60979000)

可以看到路径已经变化。

有时候光换libc是不够的,大概率ld也要换。

https://blog.csdn.net/caiqiiqi/article/details/72807952

https://blog.csdn.net/luozhaotian/article/details/79609077

1.可以直接在.gdb文件中写。然后source进去

2.利用python import gdb然后通过gdb.execute(cmd)来执行命令,同样也是source进去

例子1:https://www.cnblogs.com/dylancao/p/9252756.html

例子2:

在前一段时间的sudo cve中,我们使用gdb脚本编写了一个基于gdb的fuzz程序,也拿上来给大家做一个参考。

1.通过 gdb.execute(cmd)来执行gdb命令。

2.关闭分页并打开log输出,指定输出到./gdb.output

3.在gdb中通过执行set env来调整环境变量的值。

4.每次crash完都运行backtrace导出bt信息到log文件。

堆利用的一些小trick总结

fd:point to the next free chunk

bk:point to the preovious free chunk

如果有堆溢出,考虑overlap掉下面的,然后通过上面的chunk做溢出劫持下方的fd、bk、next指针。

虽然清空了table中的指针,但是我们可以先free一次放进unsortbin,然后再申请出来,若使用的是malloc而不是calloc,或者没有特定的清楚堆块内容的操作时,fd和bk指针会留在里面(相当于进bins里溜了一圈带了俩指针出来。。),然后直接show出来。

注意检查add、edit、del函数中index的合法性(是否为unsigned int?是否可以越界?)

注意映射位置,heap的mapped到的位置很接近bss段,看是否可以越界过去。

当有off-by-one时考虑,做overlap,假如可以修改pre_in_use位,那么比如说先申请三个chunk,0,1,2(其中0号为大chunk可直接进入unsortbin,1号为0x68小chunk,2号为大chunk可直接进入unsortbin),先del(0),再del,add1号,对2号使用off-by-one,同时伪造pre_size位为0+1号的总size合(注意申请1号chunk大小以0x8结尾,方便off-by-one),此时指示0,1号chunk为free状态,再del(2),此时触发unsortbin的前向合并,0、1、2三个chunk被合并放入unsortbin,然后再单独del(1)使其进入tcahce(或fastbin),此时1号chunk被overlap,他既在unsortbin又再tcache。

然后对于合并放入unsortbin的chunk做切割,将fd、bk指针下放进入1号chunk(既在unsort又在tcache),注意切割的时候要错开,防止把tcache里的1返回,比如1大小为0x68,0大小为0x500,那么先申请0x500,然后申请0x78(注意不是0x68,此时要求从unsortbin中返回而不是tcahce)

以上操作结束以后,1号chunk此时在tache中,但是被下放了指向main_arena的指针,在申请0x78的时候同时做edit,则可以修改fd、bk指针的位置,比如让其指向stdout。这一方法的目的是在tcache中踩出指向main_arena的指针

a

关于进阶版,只有off-by-null,对于chunk(0,1,2)大小分别为(0x68,0x38,0x420),首先删掉#0。然后用#1做off-by-null,使#2的size:0x421→0x400,同时伪造#2的pre_size为size(#0 + #1)的和(这样unsortbin前向合并的时候才知道前面的大小到底是多少)。之后在#2的末尾造一个0x31的fake_chunk(否则由于chunk2的size的减小,堆结构会崩溃)。

然后del(2),此时#0→#2合并,#1被overlap

然后切割unsotbin中的大chunk。造成fd、bk下放到原#1的位置,由于#1是被直接overlap的,所以table仍存储了#1的ptr和size,然后show(1)泄露libc。

此种类型的题中,bss段上的全局变量并不直接储存chunk的地址,而是储存一个info struct的地址(一般与chunk一同被申请出来),而info struct记录了chunk的一些信息,一般是大小,地址等。题目通过先查找info struct再去操作具体的chunk

看info struct是否可能共用?(比如内容输入一样时只申请info struct不申请new note)

看是否可以直接show出fd、bk指针?

stdout/stderr/stdin在got表中存有真实的地址,如果是tcache pwn,可以考虑dup然后从got表劫持stdout

stdout结构:

图片描述

​ 改_flags为0xfbad1800,然后p64(0)*3,然后改_IO_write_base/ptr/end可控制输出的位置和内容,一般直接输出自己就行。

查看一下tcache_get

你会发现,当tcache取chunk的时候会出现即使计数为0,但只要next指针合法,也能取出来的情况。。。并且此时tcache计数为-1(255)

此漏洞在glibc2.31中被修复。

图片描述

简述:在高版本下对于tcache有一种stashing机制,大体就是说,当从smallbin中取大小为size的chunk时,如果发现取出来一个chunk,但是smallbin中还有剩余的chunk,那么会将剩余的chunk插入对应size的tcahce_entry中。称为:“stashing”

源码:

启动条件(假设chunk大小0x110):

1.(0x110)smallbin 中两个chunk:#1 -> #2

2.tcache_entry[0x110] 中不满(not full)

过程与结果:

劫持 #1的bk指针指向 evil_addr-0x10 ,并 保证 #1的fd指针不变

越过tcache,从smallbin中取0x110的chunk(一般是用calloc实现),此时 #2 从free状态被取出变成used#1 被tcahce_put放入对应的不满的tcache_entry中evil_addr被写入bin地址(0x7f的大数)

启动条件(假设chunk大小0x110,假设我们想分配到 evil_addr):

1.(0x110)smallbin 中两个chunk:#1 -> #2

2.tcache_entry[0x110] 中不满(not full)

过程与结果:

house_of_botcake : 假如我们有 #1 #2 两个chunk,那么通过将#1 #2 合并放入ub,然后再单独free #2,就实现了#2既在ub又在tc中,之后对#1 进行合适大小的错位切割,可以劫持#2的next指针实现任意地址申请。

House_of_botcake+ : 假如我们有 #1 #2 两个chunk(0x110),假设只能申请最大malloc 0x100,那么通过将#1 #2 合并放入ub,然后再单独free #2,就实现了#2既在ub又在tc中,但是此时做不出特别合适的切割大小来一步到位劫持 #2指针,可以考虑先切一个小的chunk,比如0x20,之后ub中的chunk会被切割,导致当我们再次add一个大chunk(0x100)时,返回给我们的chunk是 #1+0x20 ,此时如果再add 0x100,就实现了申请一个地址在:[#1+0x20,#2+0x20] 的chunk #3,然后把#3 free进tc中与#2连接,就可以劫持#2的next指针实现任意地址申请。

2.29 2.31的gadget,用来拉栈。

https://blog.csdn.net/carol2358/article/details/108351308

举个例子:

malloc(0xf8),返回0x100

malloc(0xf9),返回0x110

 
patchelf --set-rpath /root/roarctf/ ./2a1
patchelf --set-rpath /root/roarctf/ ./2a1
 
 
 
 
 
 
 
 
pwndbg> print getpid()
 
Program received signal SIGALRM, Alarm clock.
$1 = 101499
 
pwndbg> shell cat /proc/101499/maps | grep vdso
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
 
pwndbg> dump memory ./linux-vdso.so.1 0x7ffff7ffa000 0x7ffff7ffc000    # 这一步dump内存生成linux-vdso.so.1
pwndbg> q
root@ubuntu:~/roarctf# ls
2a1  core  exp.py  libc.so.6  linux-vdso.so.1
root@ubuntu:~/roarctf# file li
libc.so.6        linux-vdso.so.1 
root@ubuntu:~/roarctf# file linux-vdso.so.1             # 查看dump出来的
linux-vdso.so.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=4cd5c52e24bb8daba3f767be4ca9084487a64e78, stripped
root@ubuntu:~/roarctf#
pwndbg> print getpid()
 
Program received signal SIGALRM, Alarm clock.
$1 = 101499
 
pwndbg> shell cat /proc/101499/maps | grep vdso
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
 
pwndbg> dump memory ./linux-vdso.so.1 0x7ffff7ffa000 0x7ffff7ffc000    # 这一步dump内存生成linux-vdso.so.1
pwndbg> q
root@ubuntu:~/roarctf# ls
2a1  core  exp.py  libc.so.6  linux-vdso.so.1
root@ubuntu:~/roarctf# file li
libc.so.6        linux-vdso.so.1 
root@ubuntu:~/roarctf# file linux-vdso.so.1             # 查看dump出来的
linux-vdso.so.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=4cd5c52e24bb8daba3f767be4ca9084487a64e78, stripped
root@ubuntu:~/roarctf#
 
 
 
 
import gdb
import random
#from pwn import *
# POC
# sudo gdb --args sudoedit -s '\' `perl -e 'print "A" x 550'`
 
# 1.sudo gdb sudoedit
# 2.source gdb.py
 
 
 
def set_up():
    gdb.execute("start")
    gdb.execute("set pagination off")
    gdb.execute("set logging on ./gdb.output")
 
def over():
    gdb.execute("set logging off")
    #gdb.execute("quit")
 
def fuzz():
    set_up()
    cmd = "set env LC_ALL = %s" %("A"*4792)
    #print("fuzz1_test:",i)
    gdb.execute(cmd)
 
    cmd = "run -s '\\' `perl -e 'print \"A\" x %s'`" %(4792)
    print("fuzz1_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
def fuzz1(i,j):
    set_up()
    cmd = "set env LC_ALL = %s" %("A"*i)
    print("fuzz1_test:",i)
    gdb.execute(cmd)
 
    cmd = "run -s '\\' `perl -e 'print \"B\" x %s'`" %(j)
    print("fuzz1_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz2(i,j,k):
    set_up()
    cmd = "set env LC_ALL = %s" %("C"*i)
    print("fuzz2_test:",i)
    gdb.execute(cmd)
    cmd = "set env LOCPATH = %s" %("D"*j)
    print("fuzz2_test:",j)
    gdb.execute(cmd)
    cmd = "run -s '\\' `perl -e 'print \"E\" x %s'`" %(k)
    print("fuzz2_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz3(i,j,k):
    set_up()
    cmd = "set env LC_ALL = %s" %("F"*i)
    print("fuzz3_test:",i)
    gdb.execute(cmd)
    cmd = "set env LC_MESSAGES = %s" %("G"*j)
    print("fuzz3_test:",j)
    gdb.execute(cmd)
    cmd = "run -s '\\' `perl -e 'print \"H\" x %s'`" %(k)
    print("fuzz3_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz4(i,j,k):
    set_up()
    cmd = "set env LOCPATH = %s" %("I"*i)
    print("fuzz4_test:",i)
    gdb.execute(cmd)
    cmd = "set env LC_MESSAGES = %s" %("J"*j)
    print("fuzz4_test:",j)
    gdb.execute(cmd)
    cmd = "run -s '\\' `perl -e 'print \"K\" x %s'`" %(k)
    print("fuzz4_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz5(i,j,k,h,l,p):
    set_up()
    cmd = "set env LC_ALL = %s" %("F"*p)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "set env LC_NUMERIC C.UTF-8@%s"%("A"*i)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "set env LC_MESSAGES = %s" %("J"*l)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "set env LC_MEASUREMENT C.UTF-8@%s"%("B"*j)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "run -s '%s\\' `perl -e 'print \"K\" x %s'`" %("C"*k,h)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
 
 
 
def main():
    begin=200
    end=1000
    count=800
 
    for i in range(2000):
        fuzz5(  random.randint(5,100),
            random.randint(5,100),
            random.randint(5,100),
            random.randint(200,800),
            random.randint(5,100),
            random.randint(5,100),
            )
    gdb.execute("quit")
 
main()
import gdb
import random
#from pwn import *
# POC
# sudo gdb --args sudoedit -s '\' `perl -e 'print "A" x 550'`
 
# 1.sudo gdb sudoedit
# 2.source gdb.py
 
 
 
def set_up():
    gdb.execute("start")
    gdb.execute("set pagination off")
    gdb.execute("set logging on ./gdb.output")
 
def over():
    gdb.execute("set logging off")
    #gdb.execute("quit")
 
def fuzz():
    set_up()
    cmd = "set env LC_ALL = %s" %("A"*4792)
    #print("fuzz1_test:",i)
    gdb.execute(cmd)
 
    cmd = "run -s '\\' `perl -e 'print \"A\" x %s'`" %(4792)
    print("fuzz1_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
def fuzz1(i,j):
    set_up()
    cmd = "set env LC_ALL = %s" %("A"*i)
    print("fuzz1_test:",i)
    gdb.execute(cmd)
 
    cmd = "run -s '\\' `perl -e 'print \"B\" x %s'`" %(j)
    print("fuzz1_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz2(i,j,k):
    set_up()
    cmd = "set env LC_ALL = %s" %("C"*i)
    print("fuzz2_test:",i)
    gdb.execute(cmd)
    cmd = "set env LOCPATH = %s" %("D"*j)
    print("fuzz2_test:",j)
    gdb.execute(cmd)
    cmd = "run -s '\\' `perl -e 'print \"E\" x %s'`" %(k)
    print("fuzz2_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz3(i,j,k):
    set_up()
    cmd = "set env LC_ALL = %s" %("F"*i)
    print("fuzz3_test:",i)
    gdb.execute(cmd)
    cmd = "set env LC_MESSAGES = %s" %("G"*j)
    print("fuzz3_test:",j)
    gdb.execute(cmd)
    cmd = "run -s '\\' `perl -e 'print \"H\" x %s'`" %(k)
    print("fuzz3_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz4(i,j,k):
    set_up()
    cmd = "set env LOCPATH = %s" %("I"*i)
    print("fuzz4_test:",i)
    gdb.execute(cmd)
    cmd = "set env LC_MESSAGES = %s" %("J"*j)
    print("fuzz4_test:",j)
    gdb.execute(cmd)
    cmd = "run -s '\\' `perl -e 'print \"K\" x %s'`" %(k)
    print("fuzz4_test:",cmd)
    gdb.execute(cmd)
    gdb.execute("bt")
    over()
 
def fuzz5(i,j,k,h,l,p):
    set_up()
    cmd = "set env LC_ALL = %s" %("F"*p)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "set env LC_NUMERIC C.UTF-8@%s"%("A"*i)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "set env LC_MESSAGES = %s" %("J"*l)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "set env LC_MEASUREMENT C.UTF-8@%s"%("B"*j)
    print("fuzz5_test:",cmd)
    gdb.execute(cmd)
    cmd = "run -s '%s\\' `perl -e 'print \"K\" x %s'`" %("C"*k,h)
    print("fuzz5_test:",cmd)

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 6
支持
分享
打赏 + 2.00雪花
打赏次数 1 雪花 + 2.00
 
赞赏  kanxue   +2.00 2021/02/25 感谢分享~
最新回复 (4)
雪    币: 14517
活跃值: (17538)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2021-2-24 14:44
0
雪    币: 47147
活跃值: (20445)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
辛苦了!
2021-2-25 14:59
0
雪    币: 499
活跃值: (2189)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2021-3-2 11:10
0
雪    币: 15187
活跃值: (16852)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
5
支持长期更新
2021-3-5 14:08
0
游客
登录 | 注册 方可回帖
返回
//