赞
踩
题目说编译运行这个flag.c文件即可获得flag。那我们先把flag.c文件下载下来,然后托到虚拟机里使用gcc编译一下,运行看看是否能够拿到flag。
gcc -o flag -flag.c
./flag
ok,果然拿到了flag。flag为:ctfshow{hOw_t0_us3_GCC?}。
本题是让我们阅读flag.c文件的源码,给定key为"CTFshow",编译运行之后即可获得flag。其中呢,给定key为CTFshow是个啥子意思我们现在不是很清楚。那我们就先下载flag.c文件查看一下源码吧!
flag.c文件源码:
// flag.c #include <stdio.h> #include <stdlib.h> #define BUFFER_SIZE 1024 int main() { FILE *fp; unsigned char buffer[BUFFER_SIZE]; size_t n; fp = fopen("key", "rb"); if (fp == NULL) { perror("Nothing here!"); return -1; } char output[BUFFER_SIZE * 9 + 12]; int offset = 0; offset += sprintf(output + offset, "ctfshow{"); while ((n = fread(buffer, sizeof(unsigned char), BUFFER_SIZE, fp)) > 0) { for (size_t i = 0; i < n; i++) { for (int j = 7; j >= 0; j--) { offset += sprintf(output + offset, "%d", (buffer[i] >> j) & 1); } if (i != n - 1) { offset += sprintf(output + offset, "_"); } } if (!feof(fp)) { offset += sprintf(output + offset, " "); } } offset += sprintf(output + offset, "}"); printf("%s\n", output); fclose(fp); return 0; }
代码的大致逻辑就是读取一个名为"key"的文件,然后根据该文件的内容通过一个while循环再嵌套两个for循环,具体的代码就不分析了,之后就可以输出flag了。
大家注意这个key文件是从我们本地读取的,所以我们在运行之前需要先手动创建一个key文件,根据题目的提示,key文件的内容应该为CTFshow。这样我们再编译运行flag.c文件就可以获得flag了。
echo CTFshow > key # 创建内容为CTFshow的key文件
gcc -o flag ./flag.c # 编译flag.c
./flag #运行flag文件那flag
本题目是让我们将flag.asm汇编代码编译成可执行文件再运行即可拿到flag。那我们就下载flag.asm文件编译运行呗。
nasm -f elf64 flag.asm # 将flag.asm编译成64为.o文件
ld -s -o flag flag.o # 将flag.o链接成flag可执行文件
./flag # 运行flag可执行文件拿到flag
本题是让我们使用gcc将flag.s文件编译成可执行文件。那我们就下载flag.s文件将其编译成可执行文件。
gcc -o flag flag.s # 将flag.s编译成flag可执行文件
./flag # 运行flag可执行文件拿到flag
我们先将这个pwn文件拖进虚拟机,给它加上个可执行的权限。然后再使用checksec命令查看该文件的信息。
chmod +x pwn
checksec ./pwn
可以看到,pwn文件是一个64为的可执行文件,那我们直接拖进ida64反编译下看一看里边有什么东西。
反编译出的代码:
int __cdecl main(int argc, const char **argv, const char **envp) { int v4; // [rsp+4h] [rbp-1Ch] BYREF char dest[4]; // [rsp+Ah] [rbp-16h] BYREF char buf[10]; // [rsp+Eh] [rbp-12h] BYREF unsigned __int64 v7; // [rsp+18h] [rbp-8h] v7 = __readfsqword(0x28u); setvbuf(_bss_start, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); puts(asc_D48); puts(asc_DC0); puts(asc_E40); puts(asc_ED0); puts(asc_F60); puts(asc_FE8); puts(asc_1080); puts(" * ************************************* "); puts(aClassifyCtfsho); puts(" * Type : Linux_Security_Mechanisms "); puts(" * Site : https://ctf.show/ "); puts(" * Hint : You should understand the basic command usage of Linux! "); puts(" * ************************************* "); *(_DWORD *)dest = 790655852; v4 = 0; puts("\nHow much do you know about Linux commands? \n"); while ( 1 ) { menu(); v4 = 0; puts("\nEnter the command you want choose:(1.2.3.4 or 5)\n"); __isoc99_scanf("%d", &v4); switch ( v4 ) { case 1: system("id"); break; case 2: puts("Which directory?('/','./' or the directiry you want?)"); read(0, buf, 0xAuLL); strcat(dest, buf); system(dest); puts("Execution succeeded!"); break; case 3: sleep(1u); puts("$cat /ctfshow_flag"); sleep(1u); puts("ctfshow{"); sleep(2u); puts("... ..."); sleep(3u); puts("Your flag is ..."); sleep(5u); puts("ctfshow{flag is not here!}"); sleep(0x14u); puts("wtf?You haven't left yet?\nOk~ give you flag:\nflag is loading......"); sleep(0x1BF52u); system("cat /ctfshow_flag"); break; case 4: sleep(2u); puts("su: Authentication failure"); break; case 5: puts("See you!"); exit(-1); default: puts("command not found!"); break; } } }
我们先来分析一下代码的逻辑,首先是进入一个while循环打印出menu也就是菜单。然后是一个switch-case语句,根据我们输入的选项来执行分支语句。
我们分析源码看到case 3中有一个system(“cat /ctfshow_flag”)的语句,也就是可以读取flag的语句。但是!!!
我们来看!!!
这条sleep(0x1BF52u)语句是我们读取flag的障碍!因为这条语句它要睡眠0x1BF52秒啊,换算成10进制就是114514秒,那就是31个小时啊!!!这怎么等的了!
所以另求他法。
我们再看case 2:
case 2:
puts("Which directory?('/','./' or the directiry you want?)");
read(0, buf, 0xAuLL);
strcat(dest, buf);
system(dest);
puts("Execution succeeded!");
break;
如果我们选择的case2,首先它是输出一行字符串,然后让我们输入一行长度为0xA的字符串buf,也就是长度为9的字符串。接着把我们输入的字符串buf赋给dest,然后使用system函数将dest作为参数传入进行命令执行。
选项2的功能总的来说就是,将我们输入的字符串当作参数传入system()函数!那我们就可以直接传入cat /ctfshow_flag吗?
那当然不行的,因为代码已经限制了我们输入字符串的长度为9,所以我们得换个命令想办法读取flag。我们知道system(“/bin/sh”);是可以获得Linux的交互式shell的,正好/bin/sh的长度也没有超过9,所以我们就可以传入/bin/sh来获取交互式shell,进而手动执行cat /ctfshow_flag来get flag了!
nc 连接,开干!!!
输入2 ->输入/bin/sh->执行cat /ctfshow_flag
OK,成功拿到flag!
首先我们还是下载pwn文件拖进虚拟机加上可执行权限再使用checksec命令查看文件信息。
chmod +x pwn
checksec pwn
64位的文件,并且题目也提示我们要看源码,那我们就将pwn这个文件拉到ida64反编译下看看源码是什么。
源码:
// main int __cdecl main(int argc, const char **argv, const char **envp) { int v4; // [rsp+4h] [rbp-Ch] BYREF unsigned __int64 v5; // [rsp+8h] [rbp-8h] v5 = __readfsqword(0x28u); setvbuf(_bss_start, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); puts(s); puts(asc_B10); puts(asc_B90); puts(asc_C20); puts(asc_CB0); puts(asc_D38); puts(asc_DD0); puts(" * ************************************* "); puts(aClassifyCtfsho); puts(" * Type : Linux_Security_Mechanisms "); puts(" * Site : https://ctf.show/ "); puts(" * Hint : Do you know redirect output ? "); puts(" * ************************************* "); puts("Which is the real flag?"); __isoc99_scanf("%d", &v4); if ( v4 == 9 ) fake(); else real(); system("cat /ctfshow_flag"); return 0; }
// fake
int fake()
{
return system("echo 'flag is here'>>/ctfshow_flag");
}
// real
int real()
{
return system("echo 'flag is here'>/ctfshow_flag");
}
我们先来分析一下代码逻辑,大概流程就是需要我们输入一个值v4,看这个值v4是否等于9,如果等于9就执行fake()函数,然后再执行system函数打印出flag;如果不等于9就先执行real()函数,然后再执行system函数打印出flag。
查看源码我们可以发现啊,real()函数的内容是将"flag is here"这条字符串追加(echo >> 两个>表示追加)到ctfshow_flag文件里,而fake()函数是将"flag is here"这条字符串覆盖(echo > 一个>表示覆盖)ctfshow_flag的文件内容,这就代表如果我们输入的v4等于9的话,system函数最后输出flag的时候,屁股后面一定会有flag is here这条字符串,之后我们再把这条字符串从输出的flag删掉就是真正的flag了!而如果我们输入的不是9,system()函数就会将flag is here函数覆盖ctfshow_flag文件的内容,这样我们的flag就没了呀,所以我们nc连接之后输入9就行了!
nc连接,开干!!!
OK,成功拿到flag!
OK,我们还是先下载pwn文件托到虚拟机里加上可执行权限再使用checksec命令查看文件信息。
chmod +x pwn
checksec pwn
pwn文件依旧是64位的,直接拖进ida64看一下源码。
// main int __cdecl main(int argc, const char **argv, const char **envp) { char buf[40]; // [rsp+10h] [rbp-30h] BYREF unsigned __int64 v5; // [rsp+38h] [rbp-8h] v5 = __readfsqword(0x28u); setvbuf(_bss_start, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); puts(s); puts(asc_BF0); puts(asc_C70); puts(asc_D00); puts(asc_D90); puts(asc_E18); puts(asc_EB0); puts(" * ************************************* "); puts(aClassifyCtfsho); puts(" * Type : Linux_Security_Mechanisms "); puts(" * Site : https://ctf.show/ "); puts(" * Hint : Turn off output, how to get flag? "); puts(" * ************************************* "); if ( fork() ) { wait(0LL); sleep(3u); printf("flag is not here!"); } else { puts("give you a shell! now you need to get flag!"); fclose(_bss_start); read(0, buf, 0x20uLL); system(buf); } return 0; }
// fork
// attributes: thunk
__pid_t fork(void)
{
return fork();
}
这道题目前还没有想出来办法解决,因为它把输出流给关掉了,也就是我们执行命令得不到回显了,而且ping命令也没有也不能用dns带外,nc命令也没有,也不能反弹到外网服务器!哎,。。。。我再想想办法~ _ ~
// 2023/06/21 更新
根据评论区里师傅的意见,关闭了输出流,我们nc连接之后可以使用 >&0定向到输入流,我们就来尝试一下。
首先我们先来看一下所在路径:
pwd >&0
可以看到我们输入命令之后,给我们返回了/,代表我们所处根路径。
我们再重新nc连接(因为这个程序给我们执行命令的环境不是交互式的,所以我们需要再次nc连接执行命令),使用ls >&0查看根目录。
ls >&0
OK,我们发现了flag,再使用cat ctfshow_flag >&0进行读取即可。
cat ctfshow_flag >&0
好的这样我们就拿到了本题的flag。
这里呢,希望大家,在学习的过程中啊,也可以多多尝试记录自己学习的过程,分享自己的理解,有不会的知识点也可以写在上面和志同道合的师傅们一起交流学习,就像这道题目pwn19,刚一开始不会解,然后就有一位热心的师傅在评论区回复帮助,真的很感谢师傅的帮助。所以大家还是跟其他人多多交流心得体会,这样我们的能力就会得到很大的提高。另外,感谢大家的支持与帮助,希望在pwn的学习之路,我们可以一起前行!!!^_^
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。