本文主要记录一些个人在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的指针
关于进阶版,只有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
pwndbg> q
root@ubuntu:~
/
roarctf
2a1
core exp.py libc.so.
6
linux
-
vdso.so.
1
root@ubuntu:~
/
roarctf
libc.so.
6
linux
-
vdso.so.
1
root@ubuntu:~
/
roarctf
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
pwndbg> q
root@ubuntu:~
/
roarctf
2a1
core exp.py libc.so.
6
linux
-
vdso.so.
1
root@ubuntu:~
/
roarctf
libc.so.
6
linux
-
vdso.so.
1
root@ubuntu:~
/
roarctf
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
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"
)
def
fuzz():
set_up()
cmd
=
"set env LC_ALL = %s"
%
(
"A"
*
4792
)
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
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"
)
def
fuzz():
set_up()
cmd
=
"set env LC_ALL = %s"
%
(
"A"
*
4792
)
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直播授课