forked from scnu-sloth/hsctf-2019-freshmen
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
684 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
## 概括 | ||
|
||
- 出题人:罗裴然 | ||
- 出题人:Payne | ||
- 类型:Crypto | ||
- 关键字:SM3、爆破 | ||
- Flag:`HSCTF{C00l_Sm3_Cr@cker}` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# 欢迎您使用华师币 | ||
|
||
## 概括 | ||
|
||
- 出题人:Payne | ||
- 类型:MISC | ||
- 关键字:以太坊、智能合约、Solidity | ||
- Flag:`HSCTF{H@pPy_ETher-Neeetw0rk!}` | ||
|
||
## 题目内容 | ||
|
||
欢迎您使用华师币!国内首创校园实名区块链电子代币~ | ||
|
||
## 项目结构 | ||
|
||
- coin.sol: Solidity智能合约源代码 | ||
- website/: 网页客户端 | ||
- website/abi.js: 智能合约的ABI | ||
- website/app.js: 客户端源代码 | ||
- website/index.html: 网页源代码 | ||
|
||
> 请使用 Remix 编译智能合约, 配置规则: | ||
> - Compiler: 0.4.26 | ||
> - EVM Version: byzantium | ||
> | ||
> 或者使用[这个配置好的链接](https://remix.ethereum.org/#optimize=true&evmVersion=byzantium&version=soljson-v0.4.26+commit.4563c3fc.js)。 | ||
## Writeup | ||
|
||
这道题有多种解法,下面分别阐述: | ||
|
||
### 分析智能合约法 | ||
|
||
我们可以阅读客户端的源代码,发现网页客户端使用 `ethers.js`,我们很容易找到 [Ethers.js 的官方文档](https://docs.ethers.io/ethers.js/html/index.html)。 | ||
|
||
然后,我们很容易就发现智能合约的地址,可以写代码提取智能合约的源代码: | ||
|
||
```js | ||
provider.getCode(CONTRACT_ADDR).then(v => console.log(v)); | ||
``` | ||
|
||
使用 [https://ethervm.io/decompile](https://ethervm.io/decompile) 分析一波源代码。 | ||
|
||
找到两个异常的 `PUSH` 指令: | ||
|
||
``` | ||
02F2 7F PUSH32 0x436165736172214b564657497b4b407353625f48576b68752d51686868777a30 | ||
031A 7F PUSH32 0x756e217d00000000000000000000000000000000000000000000000000000000 | ||
``` | ||
|
||
解码: | ||
|
||
```javascript | ||
function hex2a(hexx) { | ||
var hex = hexx.toString();//force conversion | ||
var str = ''; | ||
for (var i = 0; (i < hex.length && hex.substr(i, 2) !== '00'); i += 2) | ||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); | ||
return str; | ||
} | ||
hex2a('436165736172214B564657497B4B407353625F48576B68752D51686868777A30756E217D'); | ||
``` | ||
|
||
输出: | ||
|
||
``` | ||
"Caesar!KVFWI{K@sSb_HWkhu-Qhhhwz0un!}" | ||
``` | ||
|
||
根据提示,使用凯撒解密,偏移为3即可解出。 | ||
|
||
### 自我部署智能合约法 | ||
|
||
和前面一个方法一样,你可以获取智能合约的源代码,而且也很容易拿到智能合约的ABI。 | ||
|
||
那么我们只需要做的就是在本地搭建以太坊私网,并部署该智能合约。 | ||
|
||
使用部署该智能合约的钱包账号,运行 `test()` 函数,获得 `900000` 华师币。 | ||
|
||
再用 `bugFlag()` 函数购买 Flag,输出: | ||
|
||
``` | ||
"Caesar!KVFWI{K@sSb_HWkhu-Qhhhwz0un!}" | ||
``` | ||
|
||
根据提示,使用凯撒解密,偏移为3即可解出。 | ||
|
||
### 刷邀请-转账法 | ||
|
||
根据游戏规则,注册新账号可以获得 `500` 华师币。 | ||
每邀请一个用户,双方都可以获得 `3000` 华师币。 | ||
|
||
因此,每个账号价值 `3500` 华师币。 | ||
只需要刷 `254` 个账号就可以刷到足够购买 Flag 的华师币。 | ||
|
||
再购买 Flag,根据提示,使用凯撒解密,偏移为3即可解出。 | ||
|
||
### 抽奖法【运气】 | ||
|
||
根据游戏规则,注册新账号可以获得 `500` 华师币。 | ||
每邀请一个用户,双方都可以获得 `3000` 华师币。 | ||
每一次抽奖需要 `1000` 华师币。 | ||
|
||
欧洲人可以靠中奖买 Flag。 | ||
|
||
> 数学期望是负数,因此完全看人品。 | ||
### 偷看交易结果【运气】 | ||
|
||
监控区块链各个区块的交易,直到遇到了「刷邀请-转账法」大佬提交的 `flag` 函数交易,窃取其中的交易响应。 | ||
|
||
阅读ABI可以轻易发现,里面有一个Flag事件,非常诡异。看上去像是通知Flag的事件。 | ||
|
||
> 因为我们知道以太坊是靠日志LOG响应结果的。 | ||
那么如何监听一个Flag事件,看代码: | ||
|
||
```js | ||
contract.on(contract.filters.Flag(null, null), (fromAddress, toAddress, value, event) => { | ||
$('txt-flag').innerText = value.args.flag; | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
pragma solidity >=0.4.22; | ||
|
||
// SCNU Coin | ||
contract ScnuCoin { | ||
|
||
// Account structure | ||
struct Account { | ||
uint studentId; | ||
uint balance; | ||
} | ||
|
||
mapping(address => Account) public accounts; | ||
address private owner; | ||
|
||
constructor() public { | ||
owner = msg.sender; | ||
} | ||
|
||
// Register | ||
function register(uint studentId_) public { | ||
require( | ||
studentId_ != 0, | ||
"Invalid student ID" | ||
); | ||
|
||
require( | ||
accounts[msg.sender].studentId == 0, | ||
"You are registered!" | ||
); | ||
|
||
accounts[msg.sender].studentId = studentId_; | ||
accounts[msg.sender].balance = 500; | ||
} | ||
|
||
function registerWithInvitor(uint studentId_, address invitor_) public { | ||
require( | ||
studentId_ != 0, | ||
"Invalid student ID" | ||
); | ||
|
||
require( | ||
accounts[msg.sender].studentId == 0, | ||
"You are registered!" | ||
); | ||
|
||
require( | ||
accounts[invitor_].studentId != 0, | ||
"Invitor does not exist!" | ||
); | ||
|
||
|
||
accounts[invitor_].balance += 3000; | ||
uint balance_ = 3000; | ||
|
||
accounts[msg.sender].studentId = studentId_; | ||
accounts[msg.sender].balance = balance_ + 500; | ||
} | ||
|
||
// Transfer | ||
function transfer(address to_, uint value) public { | ||
require( | ||
accounts[to_].studentId != 0, | ||
"The target account does not exist!" | ||
); | ||
require( | ||
accounts[msg.sender].balance >= value, | ||
"You have not enough money!" | ||
); | ||
|
||
accounts[msg.sender].balance -= value; | ||
accounts[to_].balance += value; | ||
} | ||
|
||
// Donate | ||
function donate(uint value) public { | ||
require( | ||
accounts[msg.sender].balance >= value, | ||
"You have not enough money!" | ||
); | ||
|
||
accounts[msg.sender].balance -= value; | ||
} | ||
|
||
|
||
function random() private view returns (uint) { | ||
return uint(keccak256(block.timestamp, block.difficulty))%251; | ||
} | ||
|
||
// Lucky gacha | ||
function gacha() public returns(uint) { | ||
require( | ||
accounts[msg.sender].balance >= 1000, | ||
"You have not enough money to buy gacha!" | ||
); | ||
|
||
accounts[msg.sender].balance -= 1000; | ||
|
||
uint res = random() % 100; | ||
uint win = 0; | ||
if (res == 0) { | ||
win = 8888; | ||
} else if (res <= 5) { | ||
win = 2333; | ||
} else if (res <= 15) { | ||
win = 1688; | ||
} else if (res <= 30) { | ||
win = 1000; | ||
} | ||
|
||
accounts[msg.sender].balance += win; | ||
|
||
return win; | ||
} | ||
|
||
// Test | ||
function test() public { | ||
require( | ||
owner == msg.sender, | ||
"You are not the owner!" | ||
); | ||
|
||
accounts[msg.sender].balance += 900000; | ||
} | ||
|
||
event Flag (address indexed buyer, string flag); | ||
|
||
// Buy flag | ||
function buyFlag() public { | ||
require( | ||
accounts[msg.sender].balance > 888888, | ||
"You must pay 888888 HSC" | ||
); | ||
|
||
accounts[msg.sender].balance -= 888888; | ||
|
||
// flag: HSCTF{H@pPy_ETher-Neeetw0rk!} | ||
// https://www.qqxiuzi.cn/bianma/kaisamima.php | ||
// param: 3 | ||
emit Flag(msg.sender, "Caesar!KVFWI{K@sSb_HWkhu-Qhhhwz0un!}"); | ||
} | ||
} |
Oops, something went wrong.