0%

pwnable-Rookiss-write-up

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