one_byte
检查保护,开了地址随机化,没开canary
1 | [*] '/home/starrysky/beginctf/one_byte/pwn' |
查看程序,发现可以溢出一字节,也对应了题目的one-byte,程序会打开flag文件并且输出一字节,要读出flag必然需要printf,所以覆盖返回地址低位到printf处即可
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
exp如下
1 | from pwn import * |
gift_rop
打开程序,发现是静态编译的c,所以可以通过ropgadget直接获取ropchain,指令
1 | ROPgadget --binary ./pwn --ropchain |
执行指令之后会返回很多gadget地址以及一段现成的ropchain,如下
1 | from struct import pack |
这段ropchain是控制返回地址之后执行的内容,由于python语法格式,要把p +=前面的多余空格去掉,这段代码发过去会报错,因为最后一段重复执行add rax,1,长度太长了,所以就需要从返回的gadget中找到可替代的gadget,格式就是将gadget地址放在Q后面。execve的系统调用号在64位中是59,所以最后一段的目的就是把rax加到59,但是返回的gadget太多,可以用grep筛选一下
1 | ROPgadget --binary ./pwn --ropchain | grep 'rax' |
找到以下两条gadget,用这两条代替add rax, 1凑到59
1 | 0x0000000000471267 : add rax, 2 ; ret |
exp如下
1 | from pwn import * |
由于程序中有close
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
所以getshell以后还需要重定向标准输出,输入exec 1>&0即可
exec
- 代替
shell执行命令,区别是shell执行完之后会回到shell,而exec会直接退出。- 文件重定向,也就是
exec 1>&0这样将文件描述符为1的文件重定向到0上标准输出
(close(1))和标准错误(close(2)),有shell但获得不了输出,可以通过exec 1>&0重定向
unhappy
这题就是写shellcode,但是shellcode里不能含有HAPYhapy
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
推荐一个网站https://shell-storm.org/online/Online-Assembler-and-Disassembler/
要在限制下写的话可以利用sub和add去凑出要实现的代码,比如add rdi,0x61,可以写成add rdi, 0x60 add rdi,0x1,但是这题有一个简便的方法就是输入shellcode来执行read,通过syscall调用的read就没有了check的限制,但是第二次输入需要先sleep停顿一下
先试着用execve(’/bin/sh’, 0,0)来getshell
1 | add edi, 0x30 |
打到远程会发现没有cat flag的权限,但是unhappy有rws权限,也就是执行unhappy有root权限,所以把shellcode改成orw即可,直接write会报错,这里也学习到了一个知识点:
ssize_t sendfile(int out_fd,int in_fd,off_t* offset,size_t count);sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝
最终exp如下
1 | from pwn import * |
ezpwn
test command不能用cat和sh(官方wp指出test command对指令过滤不严,也可以利用test command),test filename不能输入flag,teat data功能是输入一个index,向数组该index处输入一个字符,而程序中有shell函数,所以调试一下计算返回地址到该数组之间的距离作为index,把返回地址地位改成shell函数低位即可,需要注意的是输入格式,行末要加回车不然会覆盖成回车
1 | unsigned __int64 main_loop() |
exp如下
1 | from pwn import * |
no_money
格式化字符串,但是禁用$,一般用$定位,本题中也学到一个知识点:’%p’ * n + ‘%hn’ = ‘%n$hn’,利用这一点,先泄露程序基址加上shell地址最后改返回地址低位为shell地址即可
1 | from pwn import * |
cat
开了canary但是没开pie
1 | checksec pwn |
程序中有后门函数,main中向bss段两个变量输入数据,vul中向栈中输入数据
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
重点就是绕过canary,这里学到一个知识点:strcat会从第一个\x00开始拼接,strcpy会在末尾写\x00,查看栈中数据
1 | pwndbg> stack |
利用:read3覆盖掉dest到canary末尾的\x00,strcat拼接会从canary之后开始拼接,最后利用strcpy将canary末尾恢复成\x00
exp
1 | from pwn import * |
aladdin
非常…痛苦的一题,本地和远程的偏移竟然不一样…用不了ogg,bss段上的chance=4,只能利用三次,所以这三次要先把chance改掉
1 | while ( --chance ) |
查看栈结构,利用这几个地址来泄露一些需要的值:code_base、stack、libc_base
1 | pwndbg> stack |
输入的wish是bss段的,也就是非栈上的格式化字符串,需要用栈作为跳板去改其他地方的数据,本题的利用思路就是第一次输入用于泄露地址,第二次输入用来设置跳板,第三次输入用来改printf返回地址到val中的输入,继续输入改chance值,但也不能改太大,用到的链如下
1 | 0x7fffffffdde8 —▸ 0x7fffffffded8 —▸ 0x7fffffffe260 ◂— '/home/starrysky/game_2024/beginctf/aladdin/pwn' |
修改成功后就可以无限利用fmt,但这题有沙箱,只能orw
1 | aladdin seccomp-tools dump ./pwn |
但是把栈直接改orw比较麻烦,所以可以在返回地址先构造read,然后用read读rop链实现orw
exp
1 | from pwn import * |
zeheap
存在uaf漏洞,但是edit和show受到mark的限制
1 | unsigned __int64 delete() |
将unsorted bin中的堆重复释放到tcache中,就可以利用这个堆泄露libc地址,并且将tcache中的堆和unsorted bin中的堆都申请出来就可以造成堆块复用,其中一个释放到tcache再用另一个改fd为free_hook最后申请出来改成system
exp
1 | from pwn import * |