声明:都是官方wp,只是加了我不懂的地方

pwn1

Hint:提供一个后门函数,连上即可得到flag

远程:
nc ip 端口
即可打通

本地:

checksec检查保护

$ chmod +x pwn
$ checksec pwn
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

64位仅关闭Canary保护

用64位IDA打开查看main函数(按F5进入反汇编或者Tab键)

int __fastcall main(int argc, const char **argv, const char **envp)
{
setvbuf(_bss_start, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
puts(s);
puts(asc_950);
puts(asc_9D0);
puts(asc_A60);
puts(asc_AF0);
puts(asc_B78);
puts(asc_C10);
puts(" * ************************************* ");
puts(aClassifyCtfsho);
puts(" * Type : Test ");
puts(" * Site : https://ctf.show/ ");
puts(" * Hint : You only need to connect to the remote address with NC to get the flag!");
puts(" * ************************************* ");
puts("I think now it is necessary to test whether your NC is useful! ");
system("cat /ctfshow_flag");
return 0;
}

程序直接执行了后门函数

程序中执行的system函数里面的命令就是在shell中执行了此命令,因此

当远程环境的根目录中存在此文件就会直接将其读出来

$ ./pwn
* *************************************
* Classify: CTFshow --- PWN --- 入门
* Type : Test
* Site : https://ctf.show/
* Hint : You only need to connect to the remote address with NC to get the flag!
* *************************************
I think now it is necessary to test whether your NC is useful!
cat: /ctfshow_flag: No such file or directory

在本地测试运行,根目录没有这个文件

可以尝试在根目录写一个与其文件名相同的文件进行测试

$ sudo bash -c "echo 'flag{just_test_my_process}'> /ctfshow_flag"   #通过 bash -c 让整个命令(包括重定向)在提升权限的 shell 中执行

再运行程序就能得到本地写入的flag值

$ ./pwn
* *************************************
* Classify: CTFshow --- PWN --- 入门
* Type : Test
* Site : https://ctf.show/
* Hint : You only need to connect to the remote address with NC to get the flag!
* *************************************
I think now it is necessary to test whether your NC is useful!
flag{just_test_my_process}

pwn2

Hint:给你一个shell,这次需要你自己去获得flag

远程:
nc ip 端口

本地:

checksec检查保护

$ chmod +x pwn
$ checksec pwn
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

64位仅关闭Canary保护

用64位IDA打开查看main函数(按F5进入反汇编或者Tab键)

int __fastcall main(int argc, const char **argv, const char **envp)
{
setvbuf(_bss_start, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
logo();
puts(" Now, you can use 'cat /ctfshow_flag' to get flag! ");
system("/bin/sh");
return 0;
}

可以看到,程序依旧是非常的简单,但是这次system函数里面的字符串变成了“/bin/sh”,那么直接运行它会发生什么呢?

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Nov 23 2023 /bin/sh -> dash

它指向dash

那system(“/bin/sh”);的工作原理又是什么呢?system()函数先fork一个子进程,在这个子进程中调用/bin/sh -c来执行command指定的命

令。/bin/sh在系统中一般是个软链接,指向dash或者bash等常用的shell,-c选项是告诉shell从字符串command中读取要执行的命令(shell将扩展command中的任何特殊字符)。父进程则调用waitpid()函数来为变成僵尸的子进程收尸,获得其结束状态,然后将这个结束状态返回给system()函数的调用者。那么也就是说执行完这个后它就会返回一个shell给函数的调用者:

$ ./pwn
* *************************************
* Classify: CTFshow --- PWN --- 入门
* Type : Test
* Site : https://ctf.show/
* Hint : To give you a shell!
* *************************************
Now, you can use 'cat /ctfshow_flag' to get flag!
$ id
uid=1000(helloctfos) gid=1000(helloctfos) groups=1000(helloctfos),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev),999(docker)
$ whoami
helloctfos
$ ls
$ cat /ctfshow_flag

也就达到了远程命令执行的效果了,读取flag只需要执行“cat /ctfshow_flag”命令即可,在做题过程中现在可以了解到:system(“cat /ctfshow_flag”);system(“/bin/sh”);这一类的我们称之为后门函数,再后续利用过程中我们要尽可能找到或者构造出来,后续会逐步完善。

pwn3

Hint:哪一个函数才能读取flag?

远程:
nc ip 端口

本地:

$ ./pwn
* *************************************
* Classify: CTFshow --- PWN --- 入门
* Type : Test
* Site : https://ctf.show/
* Hint : Choice the right backdoor !
* *************************************
[*] level up ! Let's go !
You can call the following function:
1._start
2.main
3.hello_ctfshow
4.ctfshow('echo /ctfshow_flag')
5.print('/ctfshow_flag')
6.system('cat /ctfshow_flag')
7.puts('/ctfshow_flag')
8.exit
Your choice is :

6
flag{just_test_my_process}

通过前面的分析,可以很明显的看出来选项”6”是所需要的后门函数,其他的均不会得到所想要的flag。但是很多时候它回显给你的并不一定是它一定就执行了此类,就是有可能是“虚晃一枪”,它回显给你说它执行了这个,但是实际上没有执行的情况,最终情况还得自己多进行分析。

checksec检查保护

$ chmod +x pwn
$ checksec pwn
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

64位,保护全开

用64位IDA打开查看main函数(按F5进入反汇编或者Tab键)

int __fastcall main(int argc, const char **argv, const char **envp)
{
const char **envp_1; // rdx
_BYTE argva[12]; // [rsp+4h] [rbp-Ch] BYREF

*(_QWORD *)&argva[4] = __readfsqword(0x28u);
setvbuf(_bss_start, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
logo();
puts("[*] level up ! Let's go ! ");
menu();
puts("Your choice is :\n");
__isoc99_scanf("%d", argva);
switch ( *(_DWORD *)argva )
{
case 1:
puts("start");
break;
case 2:
main((int)"%d", (const char **)argva, envp_1);
break;
case 3:
printf("Hello CTFshow");
break;
case 4:
ctfshow();
break;
case 5:
printf("/ctfshow_flag");
break;
case 6:
system_func();
break;
case 7:
puts("/ctfshow_flag");
break;
case 8:
exit(0);
default:
puts("Invalid input");
break;
}
return 0;
}

可以看到程序的流程还是很简单,先回显出一个字符串说难度升级,开始,然后有一个菜单,再让你输入选项,会进入对应的分支。跟进menu:

int menu()
{
puts("You can call the following function:");
puts("1._start");
puts("2.main");
puts("3.hello_ctfshow");
puts("4.ctfshow('echo /ctfshow_flag')");
puts("5.print('/ctfshow_flag')");
puts("6.system('cat /ctfshow_flag')");
puts("7.puts('/ctfshow_flag')");
return puts("8.exit");
}

我们已经了解到后门函数是这里的选项6,我们首先就跟进看它是否执行了它:

int system_func()
{
return system("cat /ctfshow_flag");
}

可以看到,确实是执行了。那么也就能得到需要的flag了。

pwn4

Hint: 或许需要先得到某个神秘字符

远程:
nc ip 端口

本地:

checksec检查保护

$ chmod +x pwn
$ checksec pwn
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

64位,保护全开

用64位IDA打开查看main函数(按F5进入反汇编或者Tab键)

int __fastcall main(int argc, const char **argv, const char **envp)
{
char s1[11]; // [rsp+1h] [rbp-1Fh] BYREF
char s2[12]; // [rsp+Ch] [rbp-14h] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-8h]

v6 = __readfsqword(0x28u);
setvbuf(_bss_start, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
strcpy(s1, "CTFshowPWN");
logo();
puts("find the secret !");
__isoc99_scanf("%s", s2);
if ( !strcmp(s1, s2) )
execve_func();
return 0;
}

大致流程如下:

首先将字符串 “CTFshowPWN” 复制到 s1 变量中。

接着,使用 puts 函数输出字符串 “find the secret !”。

紧接着,通过 __isoc99_scanf 函数从用户输入中读取一个字符串到 s2 变量中。

最后,通过 strcmp 函数比较 s1 和 s2 的内容是否相同。如果相同,则调用 execve_func 函数。

跟进execve_func():

unsigned __int64 execve_func()
{
char *argv[3]; // [rsp+0h] [rbp-20h] BYREF
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
argv[0] = "/bin/sh";
argv[1] = 0;
argv[2] = 0;
execve("/bin/sh", argv, 0);
return __readfsqword(0x28u) ^ v2;
}

可以看到将字符串 “/bin/sh” 赋值给 argv 变量。

然后,将 v2 和 v3 初始化为 0。

通过调用 execve 系统调用来执行 /bin/sh shell。

因此这里的execve_func也就是我们所谓的一个后门函数了

execve 本身并不是一个后门函数。实际上, execve 是一个标准的系统调用函数,用于在 Linux和类 Unix 系统中执行一个新的程序。它的原型如下:

int execve(const char *filename, char *const argv[], char *const envp[]);

该函数接受三个参数:

  1. filename :要执行的程序的文件名或路径。
  2. argv :一个以 NULL 结尾的字符串数组,表示传递给新程序的命令行参数。
  3. envp :一个以 NULL 结尾的字符串数组,表示新程序的环境变量。

当调用 execve 函数时,它会将当前进程替换为新程序的代码,并开始执行新程序。新程序接收idargv 和 envp 作为命令行参数和环境变量。

在加入某些参数后就可以达到我们所需要的后门函数的效果。

$ ./pwn
* *************************************
* Classify: CTFshow --- PWN --- 入门
* Type : Test
* Site : https://ctf.show/
* Hint : New backdoor !
* *************************************
find the secret !
CTFshowPWN
$

因此,针对本题来讲,当输入“CTFshowPWN”后就能够得到所需要的shell了,进而就能拿到所需的flag。