无法覆盖EIP寄存器

问题描述 投票:0回答:1

背景

我正在 Linux 中创建一个容易受到缓冲区溢出攻击的应用程序来练习此类漏洞(创建和利用),但我陷入了这部分。

我的问题

尽管输入了一堆“A”字母(大约1000个),但我无法覆盖EIP寄存器,而且,超出缓冲区限制时程序也不会崩溃。

此代码的目的是输入验证码(非易受攻击的字段),然后向“用户”请求电子邮件(无需验证...这是易受攻击的字段)。

目标

能够利用上述代码上的缓冲区溢出,修改尽可能少的方式来验证“有效”用户(首先是有效代码,然后是电子邮件)。

代码

源代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 6000
#define BUFFER_SIZE 256
#define CODE "xxxxxxxxxxx"

void handle_client(int client_socket) {
    char buffer[BUFFER_SIZE];
    char email[64];

    send(client_socket, "Ingrese el código: ", 19, 0);
    recv(client_socket, buffer, BUFFER_SIZE, 0);

    if (strncmp(buffer, CODE, strlen(CODE)) == 0) {
        send(client_socket, "Código correcto. Ingrese su correo: ", 36, 0);
        recv(client_socket, email, 256, 0);
    scanf("%s", email);
        printf("Correo recibido: %s\n", email);
    } else {
        send(client_socket, "Código incorrecto.\n", 19, 0);
    }

    close(client_socket);
}

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t addr_size;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket < 0) {
        perror("Error al crear el socket");
        exit(1);
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Error al enlazar el socket");
        close(server_socket);
        exit(1);
    }

    if (listen(server_socket, 5) < 0) {
        perror("Error al escuchar en el socket");
        close(server_socket);
        exit(1);
    }
    printf("Servidor escuchando en el puerto %d\n", PORT);

    while (1) {
        addr_size = sizeof(client_addr);
        client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &addr_size);
        if (client_socket < 0) {
            perror("Error al aceptar la conexión");
            continue;
        }
        printf("Conexión recibida de %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        handle_client(client_socket);
    }

    close(server_socket);
    return 0;
}

编译命令

gcc -m32 -fno-stack-protector source.c -o server -z execstack

实际行为

现在我正在用GDB调试它,这是插入有效负载时寄存器的输出:

gdb-peda$ info registers
eax            0x100               0x100
ecx            0xffffcd20          0xffffcd20
edx            0x100               0x100
ebx            0x4                 0x4
esp            0xffffccb8          0xffffccb8
ebp            0x0                 0x0
esi            0x0                 0x0
edi            0x0                 0x0
eip            0xf7fc657b          0xf7fc657b <__kernel_vsyscall+11>
eflags         0x246               [ PF ZF IF ]
cs             0x23                0x23
ss             0x2b                0x2b
ds             0x2b                0x2b
es             0x2b                0x2b
fs             0x0                 0x0
gs             0x63                0x63

据我了解,EIP 寄存器应该是 0x41414141。然而事实并非如此。

我的调查

我尝试了很多东西,大部分代码是由 ChatGPT 创建的(由于尝试手动解决它非常耗时)。

我尝试用以下函数编译它,但没有人工作:

  • 获取(编译问题)
  • strcpy
  • scanf(实际的)
  • strcmp

此外,我尝试编写 Windows 环境的代码以便稍后使用 wine 执行它,但我无法调试它,因为我无法为此特定应用程序禁用 DEP(数据执行保护)。

在这一点上,我可以得出结论,这个问题是通过它的逻辑来完成的,而不是它的函数或执行......

额外

有没有办法对代码进行散列并验证它对输入进行散列以验证它们?我不知道这个是否可以通过使用ghidra等取证工具来揭示......

编辑

2024 年欧洲中部夏令时间 10 月 7 日星期一 08:35:56

我将添加 ESP 寄存器的结果和提交有效负载时应用程序状态的 GDB 输出。

我可以看到有效负载,但 EIP 没有修改。

GDB断点输出

[----------------------------------registers-----------------------------------]
EAX: 0x100 
EBX: 0x56558ff4 --> 0x3ef0 
ECX: 0xffffcd20 ('A' <repeats 200 times>...)
EDX: 0x0 
ESI: 0xd8fe 
EDI: 0xf7ffcb60 --> 0x0 
EBP: 0xffffce68 --> 0xffffcec8 --> 0x0 
ESP: 0xffffcd20 ('A' <repeats 200 times>...)
EIP: 0x565562ef (<handle_client+146>:   sub    esp,0x8)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x565562e4 <handle_client+135>:  push   DWORD PTR [ebp+0x8]
   0x565562e7 <handle_client+138>:  call   0x56556100 <recv@plt>
   0x565562ec <handle_client+143>:  add    esp,0x10
=> 0x565562ef <handle_client+146>:  sub    esp,0x8
   0x565562f2 <handle_client+149>:  lea    eax,[ebp-0x148]
   0x565562f8 <handle_client+155>:  push   eax
   0x565562f9 <handle_client+156>:  lea    eax,[ebx-0x1f9a]
   0x565562ff <handle_client+162>:  push   eax
[------------------------------------stack-------------------------------------]
0000| 0xffffcd20 ('A' <repeats 200 times>...)
0004| 0xffffcd24 ('A' <repeats 200 times>...)
0008| 0xffffcd28 ('A' <repeats 200 times>...)
0012| 0xffffcd2c ('A' <repeats 200 times>...)
0016| 0xffffcd30 ('A' <repeats 200 times>...)
0020| 0xffffcd34 ('A' <repeats 200 times>...)
0024| 0xffffcd38 ('A' <repeats 200 times>...)
0028| 0xffffcd3c ('A' <repeats 200 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x565562ef in handle_client ()

ESP寄存器输出(命令:

x/100x $esp

0xffffcd20: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd28: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd30: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd38: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd40: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd48: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd50: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd58: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd60: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd68: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd70: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd78: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffcd80: 0x41    0x41    0x41    0x41

2024 年欧洲中部夏令时间 10 月 7 日星期一晚上 10:11:21

正如 dbush 提到的,缓冲区可能位于堆栈上的电子邮件之后。 我快速检查了一下,确实如此...

编译命令

gcc -g -m32 -fno-stack-protector testlin.c -o server -z execstack

变量的内存位置

gdb-peda$ print &buffer
$1 = (char (*)[256]) 0xffffcd50
gdb-peda$ print &email
$2 = (char (*)[64]) 0xffffcd10

堆栈视图

gdb-peda$ x/100xb 0xffffcd10
0xffffcd10: 0x61    0x73    0x64    0x61    0x73    0x64    0x0a    0x35
0xffffcd18: 0x39    0x34    0x33    0x38    0x80    0xcd    0xff    0xff
0xffffcd20: 0x1b    0x71    0x55    0x56    0x2c    0xce    0xf8    0xf7
0xffffcd28: 0xb8    0xce    0xff    0xff    0xac    0x6c    0xdb    0xf7
0xffffcd30: 0x26    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xffffcd38: 0xb8    0xce    0xff    0xff    0x80    0xcd    0xff    0xff
0xffffcd40: 0x1b    0x71    0x55    0x56    0x2c    0xce    0xf8    0xf7
0xffffcd48: 0xb8    0xce    0xff    0xff    0x42    0x1b    0xdc    0xf7
0xffffcd50: 0x62    0x70    0x68    0x79    0x73    0x75    0x75    0x78
0xffffcd58: 0x73    0x72    0x6e    0x6b    0x71    0x74    0x75    0x70
0xffffcd60: 0x66    0x77    0x63    0x79    0x0a    0x00    0x00    0x00
0xffffcd68: 0x64    0xce    0xff    0xff    0x98    0xcd    0xff    0xff
0xffffcd70: 0xe4    0xcf    0xff    0xf7
c buffer buffer-overflow
1个回答
0
投票

我通过交换声明变量

buffer
email
解决了这个问题。还可以添加/删除代码中的其他内容:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 6000
#define BUFFER_SIZE 256
#define CODE "xxxxxxxxxxxxxxxxxxxx"

int handle_client(int client_socket) {
    char email[64];
    char buffer[BUFFER_SIZE];

    send(client_socket, "Ingrese el código: ", 19, 0);
    recv(client_socket, buffer, BUFFER_SIZE, 0);

    if (strncmp(buffer, CODE, strlen(CODE)) == 0) {
        send(client_socket, "Código correcto. Ingrese su correo: ", 36, 0);
        recv(client_socket, email, 256, 0);
        printf("Correo recibido: %s\n", email);
    } else {
        send(client_socket, "Código incorrecto.\n", 19, 0);
    }

    close(client_socket);
    return 0;
}

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t addr_size;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket < 0) {
        perror("Error al crear el socket");
        exit(1);
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Error al enlazar el socket");
        close(server_socket);
        exit(1);
    }

    if (listen(server_socket, 5) < 0) {
        perror("Error al escuchar en el socket");
        close(server_socket);
        exit(1);
    }
    printf("Servidor escuchando en el puerto %d\n", PORT);

    while (1) {
        addr_size = sizeof(client_addr);
        client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &addr_size);
        if (client_socket < 0) {
            perror("Error al aceptar la conexión");
            continue;
        }
        printf("Conexión recibida de %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        handle_client(client_socket);
    }

    close(server_socket);
    return 0;
}

© www.soinside.com 2019 - 2024. All rights reserved.