PWNABLE write up[Rookiss]
Brainfuck
一个c语言的brainfuck解释器,这题有点技巧,在leak阶段我也想了好久好久。
关于brainfuck,是一种小型计算机语言,之前也接触过一些bf编码的东西
程序的do_brainfuck函数与表中除[ ]不支持外基本一致
即使用brainfuck来控制p指针对内存进行读写操作
put函数位置:0x0804a0a0-0x88=0804a018
泄露put函数地址时每次>输出一个字节内容,泄露完put后p的位置在0x0804A01C
覆写putchar函数位置 0x080AA01C+0x14=0x80AA030 为main函数
覆写fgets函数位置0x0804A010 为system函数
覆写memset函数位置 0x0804A02C 为gets函数
md5 calculator
md5计算器,程序会先base64解码输入字符,再进行md5加密
先浏览一下main函数, 发现my_hash process_hash system函数 跟进my_hash函数看下
My_hash()函数是用来产生验证码的,v11是canary,在这里被用于产生验证码,于是我们可以用验证码反推canary的值(在已知随机种子的情况下)
再看加密函数部分:
Base64加密过后,1024个字节变成768个字节,但存放的v3只有0x200,即512字节,会有栈溢出。
最后的思路是:先通过验证码计算canary,再栈溢出并填充canary为正确值,最后调用system函数getshell
Canary.c
Hash.py
Simple login
栈溢出
Otp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, char* argv[]){
char fname[128];
unsigned long long otp[2];
if(argc!=2){
printf("usage : ./otp [passcode]\n");
return 0;
}
int fd = open("/dev/urandom", O_RDONLY);
if(fd==-1) exit(-1);
if(read(fd, otp, 16)!=16) exit(-1);
close(fd);
sprintf(fname, "/tmp/%llu", otp[0]);
FILE* fp = fopen(fname, "w");
if(fp==NULL){ exit(-1); }
fwrite(&otp[1], 8, 1, fp);
fclose(fp);
printf(“OTP generated.\n”);
unsigned long long passcode=0;
FILE* fp2 = fopen(fname, "r");
if(fp2==NULL){ exit(-1); }
fread(&passcode, 8, 1, fp2);
fclose(fp2);
if(strtoul(argv[1], 0, 16) == passcode){
printf("Congratz!\n");
system("/bin/cat flag");
}
else{
printf("OTP mismatch\n");
}
unlink(fname);
return 0;
}
本题算是用了一些技巧,关键点在于ulimit的考察
这里对ulimit的使用算是逆向思维
Tiny_easy
整个程序很简单,就四行汇编代码
程序在进入入口地址时会加载参数及环境变量
什么保护措施都没开,优先考虑的是写入shellcode执行,但没有找到合适的写入点,在参考https://eugenekolo.com/blog/pwning-tiny_easy-pwnable-kr/后发现可以将shellcode写入环境变量传入程序
Fsb
一道格式化字符串题目,分值只有20分,但却困扰了我很久
key在bss段中
最开始想的是利用格式化字符串读取或改写key的值,但是因为key不在栈中,无法使用格式化字符串利用key
思路:
1.利用格式化字符串泄露esp和ebp
2.覆写ebp使其指向sleep的got地址
3.覆写sleep的got地址为execve
Dragon
刚开始看到了隐藏关
就想着要覆写s1变量,但基本不可能,但看到了getshell函数,继续往下看
游戏逻辑是无法战胜龙的,但通过耗蓝无敌再被打回蓝可以使mama 龙的血量溢出,在战胜龙后v5变量会被释放,同时分配v2变量(大小与v5相同为0x10),这里就有了uaf漏洞
Fix
shellcode是没问题的,本地也跑得起来,所以刚开始有点困惑
ida查看程序
Shellcode存放在ebp-1Ch开始处,而shellcode的长度为23字节,即shellcode到ebp-5h处
这里的问题是多次push后,栈中的内容会污染shellcode,导致shellcode无法正常执行
这里将第二个push eax 改为leave
复习一下AT&T汇编leave :
等价于
movl %ebp, %esp
popl %ebp
这里卡住了,毕竟栈基址移动后栈发生了变化,出错挺正常的,后来参考了网上大神的wp,方法挺巧妙的
https://blog.csdn.net/kostart123/article/details/79395273
Echo1
64位elf,没有任何保护,优先考虑写入shellcode,程序在echo1函数存在明显栈溢出漏洞,
最简单的方法是把jmp esp指令的机器码写入到id处,再控制eip跳转到id执行jmp esp,从而执行写入的shellcode
Echo2
和上一题类似,64位elf,没有任何保护,优先考虑写入shellcode
在echo2函数中存在fsb,可以leak该函数栈中任意地址
Echo3函数中存在uaf
思路如下:
1、将shellcode写入name变量中
2、利用fsb leak出name变量的地址
3、利用uaf跳转到name处
因为printf函数参数不定,在64位下前6个参数由寄存器传递,超过6个才用栈传递,这里format的位置又在rbp-0x20h处,堆栈中占4个,寄存器中占6个,rbp为第11位置的值,所以使用%10$p来泄露,再利用一次uaf便可getshell
小于24字节的shellcode不太好找…参考了网上的wp
Loveletter
非常巧妙的一题
程序的流程较为清晰,最后执行了system函数,初步看来不存在栈溢出,但在protect函数中发现了问题
protect函数过滤了输入的字符,并替换为占三个字节的符号
这里就存在了栈溢出
一开始纠结如何构造绕过protect函数的命令
但无奈过滤的符号太多,这条路走不通,还是利用一下栈溢出
因为在栈中v6和v7相邻,这里溢出v6达到修改v7的目的,从而控制loveletter第一部分读入长度
控制prolog为1,这样第一部分就变成了e,我们在拼接nv sh -c bash,最后执行env sh -c bash,完成getshell