2019.08.18|pwnable记录input(大坑)

题目地址:ssh input2@pwnable.kr -p2222

题目考点:linux下的文件、进程通信原理,socket网络编程,C语言的一些琐碎的细节理解。


这个题的源码非常长,所以考点繁杂,像我这样对linux了解不多的pwn萌新是很头秃的,先贴上:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc, char* argv[], char* envp[]){
    printf("Welcome to pwnable.kr\n");
    printf("Let's see if you know how to give input to program\n");
    printf("Just give me correct inputs then you will get the flag :)\n");

    // argv
    if(argc != 100) return 0;
    if(strcmp(argv['A'],"\x00")) return 0;
    if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
    printf("Stage 1 clear!\n"); 

    // stdio
    char buf[4];
    read(0, buf, 4);
    if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
    read(2, buf, 4);
        if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
    printf("Stage 2 clear!\n");
    
    // env
    if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
    printf("Stage 3 clear!\n");

    // file
    FILE* fp = fopen("\x0a", "r");
    if(!fp) return 0;
    if( fread(buf, 4, 1, fp)!=1 ) return 0;
    if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
    fclose(fp);
    printf("Stage 4 clear!\n"); 

    // network
    int sd, cd;
    struct sockaddr_in saddr, caddr;
    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd == -1){
        printf("socket error, tell admin\n");
        return 0;
    }
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons( atoi(argv['C']) );
    if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
        printf("bind error, use another port\n");
            return 1;
    }
    listen(sd, 1);
    int c = sizeof(struct sockaddr_in);
    cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
    if(cd < 0){
        printf("accept error, tell admin\n");
        return 0;
    }
    if( recv(cd, buf, 4, 0) != 4 ) return 0;
    if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
    printf("Stage 5 clear!\n");

    // here's your flag
    system("/bin/cat flag");    
    return 0;
}

显然,按照题目的要求只要完成stage1~5就能够get flag


Stage1

// argv
    if(argc != 100) return 0;
    if(strcmp(argv['A'],"\x00")) return 0;
    if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
    printf("Stage 1 clear!\n"); 

首先了解一下C语言main函数的写法,

int     main(   int   argc   ,   char   *argv[ ]   ,   char   *envp[ ]   ) 

/*

int argc, char **argv 用于运行时,把命令行参数传入主程序。

argc — 命令行参数总个数,包括 可执行程序名。

argv[i] — 第 i 个参数。

argv[0] — 可执行程序名。

*/

envp用来取得系统的环境变量,如:在DOS下,有一个PATH变量。当你在DOS提示符下输入一个命令的时候,DOS会首先在当前目录下找这个命令的执行文件。如果找不到,则到PATH定义的路径下去找,找到则执行,找不到返回Bad   command   or   file   name 。在DOS命令提示符下键入set可查看系统的环境变量。

然后是本关的思路:

1、要求参数要等于100个
2、argv[‘A’],argv[‘B’]分别要符合要求
        实际上这里的A和B其实分别是其ascii码作为索引值,即参数第64个要为”\x00″而参数第65个要为”\x20\x0a\x0d”,这样就完成了第一部分。对于第一部分,在python脚本中是先用99个A初始化,为什么用99个A而不是100个呢,可以看到,在最后的Popen()函数中,其实是将第100个参数加上的,至于为什么是/home/input2/input呢?这是因为,对于命令行参数,argc[0]通常是脚本名的。
注意⚠️这里是有坑的。C语言和python中对于\x00有着不一样的定义:
C语言中’a’和”a”的区别:C的字符串中以字符’\0’(=’\x00’) 作为结束标志,’\0’是一个ASCII码为0的字符,它不会引起任何控制动作,也不是一个可显示的字符。可以说,C语言中\x00相当于空串,会截断字符串。
Python不支持单字符类型,单字符也在Python也是作为一个字符串使用。python字符串其实是一个固定长度的字符数组,所以不用结束标志了。修改字符串其实是另外生成一个新的。也就是说,在python中\x00和空串并不等价。
(https://blog.csdn.net/jason_cuijiahui/article/details/72511610)
这两点需要注意一下,在写exp的时候要用到。

Stage2

// stdio
    char buf[4];
    read(0, buf, 4);
    if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
    read(2, buf, 4);
        if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
    printf("Stage 2 clear!\n");

        本关采用os的pipe()函数,所以我们需要了解一下os的pipe函数:os.pipe()用于创建一个管道,返回一对文件描述符(r,w)分别为读和写。这段c代码中,还有一个需要注意的是read函数,read函数作用是从文件描述如中读取size大小的元素,并送入buffer中,这里我们看到文件描述符是0与2,回想一下pwnable第一关fd时,我们便知道这里的0其实是stdin,2是stderr,所以在python脚本的最初,我们声明了两个pipe管道,stdinr,stdinw和stderrr,stderrw,其作用就是向stdin和stderr写入题目需要的字符串,而如果直接用stdin和stderr是无法直接写入的,所以利用管道的双向读写功能来完成这一部分。

Stage3

// env
    if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
    printf("Stage 3 clear!\n");

这里要认识一下getenv()函数的作用,返回一给定的环境变量值,环境变量名可大写或小写。如果指定的变量在环境中未定义,则返回一空串。

这里是要让”\xde\xad\xbe\xef”的环境变量等于”\xca\xfe\xba\xbe”,那么在python脚本中就可以通过定义一个字典,然后让”\xde\xad\xbe\xef”对应”\xca\xfe\xba\xbe”,在Popen函数的时候,将这个字典当作env的参数就好了。

Stage4

// file
    FILE* fp = fopen("\x0a", "r");
    if(!fp) return 0;
    if( fread(buf, 4, 1, fp)!=1 ) return 0;
    if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
    fclose(fp);
    printf("Stage 4 clear!\n"); 

        接下来是第四部分,重点解读一下fread函数,该函数原型

size_t fread(void *buffer, size_t size, size_t count FILE *stream)

就是读取stream流中count个元素,每个元素size大小,读取到buffer中,在这里的c代码中,是从fp中读取一个元素,4字节大小到buf中,因此,只要在python脚本中写入4个\x00就好了。

Stage5

// network
    int sd, cd;
    struct sockaddr_in saddr, caddr;
    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd == -1){
        printf("socket error, tell admin\n");
        return 0;
    }
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons( atoi(argv['C']) );
    if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
        printf("bind error, use another port\n");
            return 1;
    }
    listen(sd, 1);
    int c = sizeof(struct sockaddr_in);
    cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
    if(cd < 0){
        printf("accept error, tell admin\n");
        return 0;
    }
    if( recv(cd, buf, 4, 0) != 4 ) return 0;
    if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
    printf("Stage 5 clear!\n");

这里就是socket编程,直接按照C语言的要求连接写入就好了

        前面都是建立socket连接,只要成功建立socket连接就不会报错,后面recv(cd, buf, 4, 0)这里,cd是之前accept返回的套接字描述符,这段socket代码是一个服务端的代码,accept返回的这个cd可以想象成服务端与客户端建立连接的一个钥匙,那么这个recv其实就很好理解了,就是服务端接受的4字节的数据并且放到buf中,第四个参数0是flag一个标志位,一般为0,这里可以不用管他。然后是一个memcmp函数,这个函数就是比较buf与”\xde\xad\xbe\xef”的前4字节的内容是否相等,相等则返回0,因此这部分就是向服务端发送”\xde\xad\xbe\xef”这个字符串即可通过。
最后一步(大坑)
先来介绍一下软链接
ln src dest
这里需要注意的是,第一,ln命令会保持每一处链接文件的同步性,也就是说,不论你改动了哪一处,其它的文件都会发生相同的变化;第二,ln的链接又软链接 和硬链接两种,软链接就是ln -s str dest,它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间,硬链接ln src dest,没有参数-s, 它会在你选定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化。
如果我们用ls察看一个目录时,会发现有的文件后面有一个@的符号,那就是一个用ln命令生成的文件,用ls -l命令去察看,就可以看到显示的link的路径了。 
        首先/home/input这个目录下没有写的权限,所以需要把脚本放在/tmp去执行,而且part 4创建文件的操作也需要在tmp下进行,但是读取flag的system(“/bin/cat flag”)中的/bin/cat flag是使用相对路径的,是无法读取到flag的,所以这里使用了软连接的方法。

但是解决了相对路径的问题后运行还是不能弹出flag,发现是因为/tmp目录下没有读的权限,但是有写的权限,就有建了一个input(随便什么都可以)目录,最后就可以弹出flag了。

软连接命令示例:ln -s ~/flag ./

有两篇写的挺好的WP贴出来:
https://www.jianshu.com/p/032a754b72d8
https://blog.csdn.net/SmalOSnail/article/details/53048109
这两篇都是用python写的exp,其他多数帖子都是C语言写的,表示感觉还是py好用。
把我的exp贴上来吧:

2019.08.17|pwnable记录bof

题目如下:

考点:经典的栈溢出题


直接下载下来扔进IDA看一下

看到了gets函数,发现了利用手段,把数字改成16进制,这里用v3覆盖掉a2的位置就可以了.

v3距离ebp的距离是0x2c也就是44个地址,然后再看一下a2和v3的距离,

距离是8

所以构造payload:

得到flag

2019.08.17|pwnable记录 collision

题目地址:ssh col@pwnable.kr -p2222

考点:小端储存,变量类型转换

看一波源码:

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


这个题的核心考点应该是char和int转换所占字节数的变化

这里有几个点:

1 读入字符存到内存中是倒序的,是因为小端存储

2 这里ip[i]表示的是读入字符的ascii码,0x44434241,然后对0x44434241这一个整体,转换成十进制整数是1145258561。因此加到res中的就是每一组字符的ascii。

32位的软件中,一个int是4个字节,一个char是20个字节,转化完成后也与循环中的5所匹配

此时构造一个passcode就可以了

然后构造一下python命令写入

./col `python -c “print ‘\x01’*16+’\xe8\x05\xd9\x1d'”`

然后get flag

这里要注意构造的字符串不能跟ASCII码有冲突,不然会被转化

2019.08.17|新坑记录pwnable.kr题目fd

假期开新坑,练了这么久题了该记录一下了。

不得不说pwn题确实挺有意思的嘿嘿嘿

先来第一个

fd

题目地址:ssh fd@pwnable.kr -p2222 (pw:guest)

核心考点:LInux IO

连上后:

先看一波源码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;

}

分析一下,大概知道题目要比较LETMEWIN和buf相等就可以拿到flag,那么想办法输入buf就可以了。

题目下方有linux IO的提示,于是乎查一下


函数解释:

1 int argc 表示的是命令行中输入的参数

2 char* argv[]表示的是包括文件名在内的参数,文件名是argv[0]

3 char* envp[]是环境变量,比如path=c:\windows之类的东西。它没有一个整数来为它记数是通过最后一个evnp[i]==NULL来表示结尾的。

4 atoi:

【函数说明】atoi() 函数会扫描参数 str 字符串,跳过前面的空白字符(例如空格,tab缩进等,可以通过 isspace() 函数来检测),直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时(‘\0’)才结束转换,并将结果返回。

 

【返回值】返回转换后的整型数;如果 str 不能转换成 int 或者 str 为空字符串,那么将返回 0。

5 read()

read()会把参数fd所指的文件传送nbyte个字节到buf指针所指的内存中。若参数nbyte为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或无可读取的数据。错误返回-1,并将根据不同的错误原因适当的设置错误码。

在这里是从fd指向的文件中读取32个字节写到Buf中。len是表示都回来的字节数。

6 strcmp

设这两个字符串为str1,str2,

若str1==str2,则返回零;

若str1>str2,则返回正数;

若str1<str2,则返回负数。

read核心要点

fd == 0为标准输入
fd == 1为标准输出
fd == 2为标准错误输出
所以我们可以使fd == 0,然后从终端输入LETMEWIN后回车


要让fd==0,直接fd=0x1234就行,但是此处的坑是,终端并不接受0x的写法,于是乎要转化成十进制的数4660

Get flag.

2019.02.21|Python Django学习记录(懒得开新帖,持续更新continue)

创建新的工程命令:django-admin startproject 项目名称

项目目录结构

manage.py:与项目进行交互的命令行工具集的入口

                    项目管理器(python manage.py查看命令)

mybolg:项目容器,包含项目最基本的一些配置,目录名称不建议修改

            __init__.py:Python中声明模块的文件,默认为空

            settings.py:总配置文件(数据库、Web应用、时间等)

            urls.py:URL配置文件

                           Django项目中所有地址(页面)都需要自己去配置其URL

            wsgi.py:Python服务器网关接口

创建应用目录(新应用记得在settings.py中添加)

migrations:数据移植(迁移)模块

        __init__.py

__init__.py

admin.py:后台管理系统配置文件

apps.py:应用配置(Django1.9之后生成)

models.py:数据模块 使用ORM框架 类似于MVC结构中的Models(模型)

tests.py:自动化测试模块(用于编写测试脚本)

views.py:!!IMPORTANT!!执行响应的代码所在模块,代码逻辑处理的主要地点,项目中大部分代码均在这里编写

创建页面响应

from djando.http import HttpResponse

def index(request):
return HttpResponse(‘Hello World!’)

创建完成后记得修改url配置

第二种URL配置方法

Templates

HTML文件 特殊的语言DTL

允许第三方模板 如:Jinja2

render(,,)

 

2018.10.14|Web组第一周周报

阅读

  • html,css,js相关知识入门

http://www.w3school.com.cn/html/index.asp

http://www.w3school.com.cn/css/index.asp

http://www.w3school.com.cn/js/index.asp

  • Linux常用命令学习

https://blog.csdn.net/tao934798774/article/details/79491951

  • C Primer Plus学习

实践

  • 搭建了自己的Blog

39.105.105.208

  • 参加了中科大CTF比赛

https://hack.lug.ustc.edu.cn/

  • 虚拟机搭建了Ubuntu系统

其他

        Web方面的知识我个人还有所欠缺,有些问题操作不了解具体原理,还需要继续努力。

        中科大的CTF对我来说挺难的,需要学的东西还有很多。