Skip to content

Instantly share code, notes, and snippets.

@aadityapurani
Last active December 7, 2020 04:41
Show Gist options
  • Save aadityapurani/9cdec7360701c34d3ae2fdc1d1d7a0b4 to your computer and use it in GitHub Desktop.
Save aadityapurani/9cdec7360701c34d3ae2fdc1d1d7a0b4 to your computer and use it in GitHub Desktop.
pbcoin - pbctf 2020 Solution and source

pbcoin

Category: Misc

Author: knapstack

Solves: 1 out of 962 teams

Description: Our team strives to stay up-to-date with latest technologies. Somebody told me that I can secure my code if I apply crypto on it and use blockchain.

Drumrolls..🥁 We are releasing pbcoin which can perform cryptographic operations on blockchain. 😎 We will be the talk of security town soon.

But, can you see if you can crack the ciphertext which I generated from this blockchain mess?

Note: Please wrap flag inside pbctf{} before submitting. Flag charset is [a-z]

Hints:

  • Look for constants to know what algorithms could have been used
  • Dynamic Debugging can help only if you can haz skills to patch the bytecode
pbctf{skillfulevmremasterz}
ctxt = 76773d393c0227660910577c213e271518791c14 (Hex Decode it)
Deets:
Compiler = 0.4.24
Instructions: Plaintext aka Flag charset is [a-z], you will know it when you see it ¯\_(ツ)_/¯
[
{
"constant": true,
"inputs": [],
"name": "getseed",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "freeflagforyounoob",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "text",
"type": "string"
}
],
"name": "coinosama",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "text",
"type": "string"
}
],
"name": "Decrypt",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "duper",
"type": "bytes"
},
{
"name": "chaiyan",
"type": "bytes"
}
],
"name": "gokusan",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "krillz",
"type": "bytes"
}
],
"name": "krilliansan",
"outputs": [
{
"name": "ret",
"type": "bytes20"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_seed",
"type": "string"
}
],
"name": "setseed",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "text",
"type": "string"
}
],
"name": "vegotosan",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "x",
"type": "bytes20"
}
],
"name": "bytes20ToString",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "source",
"type": "string"
}
],
"name": "stringToBytes",
"outputs": [
{
"name": "result",
"type": "bytes"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"payable": true,
"stateMutability": "payable",
"type": "constructor"
}
]
6080604052336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550612014806100536000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306b88d15146100b45780630c0fa53e14610144578063216e2f8d146101775780632981c3ff1461025957806335781c2a146102de578063489625f4146103a95780638c1da162146104445780638da5cb5b146104ad578063af04436314610504578063c060ac53146105e6578063e106fb4b1461069b575b600080fd5b3480156100c057600080fd5b506100c961077d565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061015961081f565b60405180826000191660001916815260200191505060405180910390f35b34801561018357600080fd5b506101de600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061084c565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561021e578082015181840152602081019050610203565b50505050905090810190601f16801561024b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561026557600080fd5b506102c0600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506112b9565b60405180826000191660001916815260200191505060405180910390f35b3480156102ea57600080fd5b5061038b600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506112e8565b60405180826000191660001916815260200191505060405180910390f35b3480156103b557600080fd5b50610410600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506116ae565b60405180826bffffffffffffffffffffffff19166bffffffffffffffffffffffff1916815260200191505060405180910390f35b34801561045057600080fd5b506104ab600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050611aa0565b005b3480156104b957600080fd5b506104c2611b39565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561051057600080fd5b5061056b600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050611b5e565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156105ab578082015181840152602081019050610590565b50505050905090810190601f1680156105d85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156105f257600080fd5b5061062060048036038101908080356bffffffffffffffffffffffff19169060200190929190505050611c26565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610660578082015181840152602081019050610645565b50505050905090810190601f16801561068d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156106a757600080fd5b50610702600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050611e2e565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610742578082015181840152602081019050610727565b50505050905090810190601f16801561076f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108155780601f106107ea57610100808354040283529160200191610815565b820191906000526020600020905b8154815290600101906020018083116107f857829003601f168201915b5050505050905090565b6000807f70626374667b6f6276696f75736c795f6e6f745f7468655f666c61677d00000090508091505090565b606060008060608060008060489550854314151561086957600080fd5b859450600a60405190808252806020026020018201604052801561089c5781602001602082028038833980820191505090505b50925060276020840152600d6040840152605d6060840152602d6080840152603b60a0840152604460c0840152604d60e08401526005610100840152600261012084015260076101408401523073ffffffffffffffffffffffffffffffffffffffff1663af044363896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610970578082015181840152602081019050610955565b50505050905090810190601f16801561099d5780820380516001836020036101000a031916815260200191505b5092505050600060405180830381600087803b1580156109bc57600080fd5b505af11580156109d0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156109fa57600080fd5b810190808051640100000000811115610a1257600080fd5b82810190506020810184811115610a2857600080fd5b8151856001820283011164010000000082111715610a4557600080fd5b505092919050505093508360029080519060200190610a65929190611e3c565b50600091505b6002805460018160011615610100020316600290049050821015610be057437f01000000000000000000000000000000000000000000000000000000000000000283600a60058501811515610abc57fe5b06815181101515610ac957fe5b906020019060200201517f0100000000000000000000000000000000000000000000000000000000000000026002848154600181600116156101000203166002900481101515610b1557fe5b815460011615610b345790600052602060002090602091828204019190065b9054901a7f01000000000000000000000000000000000000000000000000000000000000000218186002838154600181600116156101000203166002900481101515610b7c57fe5b815460011615610b9b5790600052602060002090602091828204019190065b601f036101000a81548160ff021916907f0100000000000000000000000000000000000000000000000000000000000000840402179055508180600101925050610a6b565b600480805460018160011615610100020316600290049050141515610c0457600080fd5b7f660250a58689ff6ec3acb5efeb01d0ea67599efd2482c1761d5fb81c8b7b59766001023073ffffffffffffffffffffffffffffffffffffffff166335781c2a60036040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808060200180602001838103835284818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610cfe5780601f10610cd357610100808354040283529160200191610cfe565b820191906000526020600020905b815481529060010190602001808311610ce157829003601f168201915b5050838103825260138152602001807f70626374667b6e6f745f7468655f666c61677d000000000000000000000000008152506020019350505050602060405180830381600087803b158015610d5357600080fd5b505af1158015610d67573d6000803e3d6000fd5b505050506040513d6020811015610d7d57600080fd5b810190808051906020019092919050505060001916141515610d9e57600080fd5b60066002805460018160011615610100020316600290049050111561120d576002808054600181600116156101000203166002900490500390506002600482038154600181600116156101000203166002900481101515610dfb57fe5b815460011615610e1a5790600052602060002090602091828204019190065b9054901a7f010000000000000000000000000000000000000000000000000000000000000002600460038154600181600116156101000203166002900481101515610e6157fe5b815460011615610e805790600052602060002090602091828204019190065b9054901a7f0100000000000000000000000000000000000000000000000000000000000000026002600884038154600181600116156101000203166002900481101515610ec957fe5b815460011615610ee85790600052602060002090602091828204019190065b9054901a7f010000000000000000000000000000000000000000000000000000000000000002600460028154600181600116156101000203166002900481101515610f2f57fe5b815460011615610f4e5790600052602060002090602091828204019190065b9054901a7f0100000000000000000000000000000000000000000000000000000000000000021818186002600883038154600181600116156101000203166002900481101515610f9a57fe5b815460011615610fb95790600052602060002090602091828204019190065b601f036101000a81548160ff021916907f010000000000000000000000000000000000000000000000000000000000000084040217905550600260068203815460018160011615610100020316600290048110151561101457fe5b8154600116156110335790600052602060002090602091828204019190065b9054901a7f01000000000000000000000000000000000000000000000000000000000000000260046001815460018160011615610100020316600290048110151561107a57fe5b8154600116156110995790600052602060002090602091828204019190065b9054901a7f010000000000000000000000000000000000000000000000000000000000000002600280840381546001816001161561010002031660029004811015156110e157fe5b8154600116156111005790600052602060002090602091828204019190065b9054901a7f01000000000000000000000000000000000000000000000000000000000000000260046000815460018160011615610100020316600290048110151561114757fe5b8154600116156111665790600052602060002090602091828204019190065b9054901a7f010000000000000000000000000000000000000000000000000000000000000002181818600280830381546001816001161561010002031660029004811015156111b157fe5b8154600116156111d05790600052602060002090602091828204019190065b601f036101000a81548160ff021916907f010000000000000000000000000000000000000000000000000000000000000084040217905550611211565b6038565b60028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a75780601f1061127c576101008083540402835291602001916112a7565b820191906000526020600020905b81548152906001019060200180831161128a57829003601f168201915b50505050509650505050505050919050565b6000807f576173207665727920626f72656420746f20696d706c656d656e742074686973905080915050919050565b6000806000806000806040885111156113a4576002886040518082805190602001908083835b602083101515611333578051825260208201915060208101905060208303925061130e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506020604051808303816000865af1158015611377573d6000803e3d6000fd5b5050506040513d602081101561138c57600080fd5b8101908080519060200190929190505050945061150f565b600092505b8751831080156113b95750602083105b156114595782601f0360080260020a88848151811015156113d657fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027f01000000000000000000000000000000000000000000000000000000000000009004026001028517945082806001019350506113a9565b602092505b87518310801561146e5750604083105b1561150e5782603f0360080260020a888481518110151561148b57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027f010000000000000000000000000000000000000000000000000000000000000090040260010284179350828060010193505061145e565b5b7f363636363636363636363636363636363636363636363636363636363636363660010291507f5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c6001029050600285821885831860028886188887188c604051808460001916600019168152602001836000191660001916815260200182805190602001908083835b6020831015156115bd5780518252602082019150602081019050602083039250611598565b6001836020036101000a03801982511681845116808217855250505050505090500193505050506020604051808303816000865af1158015611603573d6000803e3d6000fd5b5050506040513d602081101561161857600080fd5b81019080805190602001909291905050506040518084600019166000191681526020018360001916600019168152602001826000191660001916815260200193505050506020604051808303816000865af115801561167b573d6000803e3d6000fd5b5050506040513d602081101561169057600080fd5b81019080805190602001909291905050509550505050505092915050565b600060e060020a60003504631605782b81146116c957600080fd5b5060048035018035602082019150604067ffffffffffffffc0600183011601600982820310600181146116fb57611702565b6040820191505b50776745230100efcdab890098badcfe001032547600c3d2e1f060005b82811015611a2c5760408186016000376040818503106001811461174257611749565b6080828603535b506040830381146001811461175d57611769565b60088502602051176020525b5060405b60808110156117eb5760408103516038820351186020820351600c83035118187c0100000001000000010000000100000001000000010000000100000001601f60020a8204167ffffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffe600283021617905080825250600c8101905061176d565b5060805b61014081101561186e5760808103516070820351186040820351601883035118187c0300000003000000030000000300000003000000030000000300000003601e60020a8204167ffffffffcfffffffcfffffffcfffffffcfffffffcfffffffcfffffffcfffffffc6004830216179050808252506018810190506117ef565b508160008060005b60508110156119fe5760148104600081146118a857600181146118db5760028114611903576003811461193e57611962565b602860020a8504605060020a860418935083607860020a860416935083602860020a8604189350635a8279999250611962565b605060020a8504607860020a860418935083602860020a8604189350636ed9eba19250611962565b605060020a8504607860020a860417935083602860020a860416935083605060020a8604607860020a870416179350638f1bbcdc9250611962565b605060020a8504607860020a860418935083602860020a860418935063ca62c1d692505b50601f60bb60020a8504168063ffffffe0609b60020a87041617905080840190508063ffffffff861601905080830190508060e060020a60048402510401905060a060020a8102602860020a8604179450605060020a633fffffff605260020a87041663c0000000603260020a880416170277ffffffff00ffffffff000000000000ffffffff00ffffffff861617945050600181019050611876565b5077ffffffff00ffffffff00ffffffff00ffffffff00ffffffff83860116945050505060408101905061171f565b5063ffffffff811667ffffffff00000000600860020a8304166bffffffff0000000000000000601060020a8404166fffffffff000000000000000000000000601860020a85041673ffffffff00000000000000000000000000000000602060020a860416171717179050806000526014600cf35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611afb57600080fd5b8060039080519060200190611b11929190611ebc565b50600360049080546001816001161561010002031660029004611b35929190611f3c565b5050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600080600084519250600091505b828260ff161015611c1b57848260ff16815181101515611b8a57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f01000000000000000000000000000000000000000000000000000000000000000290508060001a9050607b8110607382111615611bf55780607a0360600390505b602081141515611c0e5760078101600183026020870101535b8180600101925050611b6e565b849350505050919050565b6060806000806000606060146040519080825280601f01601f191660200182016040528015611c645781602001602082028038833980820191505090505b50945060009350600092505b6014831015611d3a578260080260020a876c01000000000000000000000000900402600102915060007f010000000000000000000000000000000000000000000000000000000000000002827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141515611d2d57818585815181101515611cf457fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535083806001019450505b8280600101935050611c70565b836040519080825280601f01601f191660200182016040528015611d6d5781602001602082028038833980820191505090505b509050600092505b83831015611e21578483815181101515611d8b57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000028184815181101515611de457fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508280600101935050611d75565b8095505050505050919050565b606060208201519050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611e7d57805160ff1916838001178555611eab565b82800160010185558215611eab579182015b82811115611eaa578251825591602001919060010190611e8f565b5b509050611eb89190611fc3565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611efd57805160ff1916838001178555611f2b565b82800160010185558215611f2b579182015b82811115611f2a578251825591602001919060010190611f0f565b5b509050611f389190611fc3565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611f755780548555611fb2565b82800160010185558215611fb257600052602060002091601f016020900482015b82811115611fb1578254825591600101919060010190611f96565b5b509050611fbf9190611fc3565b5090565b611fe591905b80821115611fe1576000816000905550600101611fc9565b5090565b905600a165627a7a7230582046d5962cc59ec980e16f081f17542bb14fdafb0fae18817b7e65a0f8da7dece30029
pragma solidity 0.4.24;
contract pbcoin{
address public owner;
string private lammer;
bytes private gol;
string private seed; //rodl should be the seed. Players will brute it if chall not on blockchain
bytes private seed_in;
/* Only Contract Creator will be the owner */
function pbcoin() public payable {
owner = msg.sender;
}
/* Only owner can set seed, as EVM isn't deployed, we want players to brute it */
function setseed(string _seed) public{
require(msg.sender == owner);
seed = _seed;
seed_in = bytes(seed);
}
function getseed() constant public returns(string){
return seed;
}
/* Encryption Routine */
function coinosama(string text) constant public returns(string) {
uint256 snum = 72;
require(block.number == snum); // Running this will not ofcourse let you test it locally lol. Comment it out if you are testing locally for challenge debug purpose
uint256 blk_num = snum;
string memory hxhx;
uint256[] memory arr = new uint[](10);
assembly {
mstore(add(arr, 32), 39)
mstore(add(arr, 64), 13)
mstore(add(arr, 96), 93)
mstore(add(arr, 128), 45)
mstore(add(arr, 160), 59)
mstore(add(arr, 192), 68)
mstore(add(arr, 224), 77)
mstore(add(arr, 256), 5)
mstore(add(arr, 288), 2)
mstore(add(arr, 320), 7)
}
// [39,13,93,45,59,68,77,5,2,7]
// string storage rotenc = this.rot7Enc(text);
hxhx = this.vegotosan(text);
gol = bytes(hxhx);
for (uint i=0; i<gol.length; i++){
gol[i] = (gol[i]^bytes1(arr[(i+5)%10]))^bytes1(block.number); // should be replaced by blk_num
}
require(seed_in.length == 4);
require(this.gokusan(bytes(seed), "pbctf{not_the_flag}") == 0x660250a58689ff6ec3acb5efeb01d0ea67599efd2482c1761d5fb81c8b7b5976);
if(gol.length > 6){ // Players will see that real flag should be > 6 obviously
uint offset = gol.length - 2; // Total length - 2
gol[offset-8] = (seed_in[2] ^ gol[offset-8]) ^ seed_in[3] ^ gol[offset-4];
gol[offset-2] = (seed_in[0] ^ gol[offset-2]) ^ seed_in[1] ^ gol[offset-6];
}
else{
assembly{
jump(0x38)
}
}
return string(gol);
}
function freeflagforyounoob() constant public returns(bytes32){
bytes32 _bytes = "pbctf{obviously_not_the_flag}";
return _bytes;
}
function vegotosan(string text) view public returns(string) {
uint256 length = bytes(text).length;
for (var i = 0; i < length; i++) {
byte char = bytes(text)[i];
//inline assembly to modify the string
assembly {
char := byte(0,char) // get the first byte
if and(gt(char,0x73), lt(char,0x7B))
{ char:= sub(0x60, sub(0x7A,char)) } // subtract from the ascii number a by the difference char is from z.
if iszero(eq(char, 0x20)) // ignore spaces
{mstore8(add(add(text,0x20), mul(i,1)), add(char,7))} // add 7 to char.
}
}
return text;
}
/* SHA-256 Hashing with HMAC */
function gokusan(bytes memory duper, bytes memory chaiyan) public pure returns (bytes32) {
bytes32 keyl;
bytes32 keyr;
uint i;
if (duper.length > 64) {
keyl = sha256(duper);
} else {
for (i = 0; i < duper.length && i < 32; i++)
keyl |= bytes32(uint(duper[i]) * 2 ** (8 * (31 - i)));
for (i = 32; i < duper.length && i < 64; i++)
keyr |= bytes32(uint(duper[i]) * 2 ** (8 * (63 - i)));
}
bytes32 threesix = 0x3636363636363636363636363636363636363636363636363636363636363636;
bytes32 fivec = 0x5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c;
return sha256(fivec ^ keyl, fivec ^ keyr, sha256(threesix ^ keyl, threesix ^ keyr, chaiyan));
}
/* Decryption Routine */
function Decrypt(string text) constant public returns(bytes32){
bytes32 _bytes = "Was very bored to implement this";
return _bytes;
}
/* SHA1 useless to increase bytecode size and noise */
function krilliansan(bytes krillz) public constant returns(bytes20 ret){
assembly {
switch div(calldataload(0), exp(2, 224))
case 0x1605782b { }
default { revert(0, 0) }
let data := add(calldataload(4), 4)
// Get the data length, and point data at the first byte
let len := calldataload(data)
data := add(data, 32)
// Find the length after padding
let totallen := add(and(add(len, 1), 0xFFFFFFFFFFFFFFC0), 64)
switch lt(sub(totallen, len), 9)
case 1 { totallen := add(totallen, 64) }
let h := 0x6745230100EFCDAB890098BADCFE001032547600C3D2E1F0
for { let i := 0 } lt(i, totallen) { i := add(i, 64) } {
// Load 64 bytes of data
calldatacopy(0, add(data, i), 64)
// If we loaded the last byte, store the terminator byte
switch lt(sub(len, i), 64)
case 1 { mstore8(sub(len, i), 0x80) }
// If this is the last block, store the length
switch eq(i, sub(totallen, 64))
case 1 { mstore(32, or(mload(32), mul(len, 8))) }
// Expand the 16 32-bit words into 80
for { let j := 64 } lt(j, 128) { j := add(j, 12) } {
let temp := xor(xor(mload(sub(j, 12)), mload(sub(j, 32))), xor(mload(sub(j, 56)), mload(sub(j, 64))))
temp := or(and(mul(temp, 2), 0xFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE), and(div(temp, exp(2, 31)), 0x0000000100000001000000010000000100000001000000010000000100000001))
mstore(j, temp)
}
for { let j := 128 } lt(j, 320) { j := add(j, 24) } {
let temp := xor(xor(mload(sub(j, 24)), mload(sub(j, 64))), xor(mload(sub(j, 112)), mload(sub(j, 128))))
temp := or(and(mul(temp, 4), 0xFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFC), and(div(temp, exp(2, 30)), 0x0000000300000003000000030000000300000003000000030000000300000003))
mstore(j, temp)
}
let x := h
let f := 0
let k := 0
for { let j := 0 } lt(j, 80) { j := add(j, 1) } {
switch div(j, 20)
case 0 {
// f = d xor (b and (c xor d))
f := xor(div(x, exp(2, 80)), div(x, exp(2, 40)))
f := and(div(x, exp(2, 120)), f)
f := xor(div(x, exp(2, 40)), f)
k := 0x5A827999
}
case 1{
// f = b xor c xor d
f := xor(div(x, exp(2, 120)), div(x, exp(2, 80)))
f := xor(div(x, exp(2, 40)), f)
k := 0x6ED9EBA1
}
case 2 {
// f = (b and c) or (d and (b or c))
f := or(div(x, exp(2, 120)), div(x, exp(2, 80)))
f := and(div(x, exp(2, 40)), f)
f := or(and(div(x, exp(2, 120)), div(x, exp(2, 80))), f)
k := 0x8F1BBCDC
}
case 3 {
// f = b xor c xor d
f := xor(div(x, exp(2, 120)), div(x, exp(2, 80)))
f := xor(div(x, exp(2, 40)), f)
k := 0xCA62C1D6
}
// temp = (a leftrotate 5) + f + e + k + w[i]
let temp := and(div(x, exp(2, 187)), 0x1F)
temp := or(and(div(x, exp(2, 155)), 0xFFFFFFE0), temp)
temp := add(f, temp)
temp := add(and(x, 0xFFFFFFFF), temp)
temp := add(k, temp)
temp := add(div(mload(mul(j, 4)), exp(2, 224)), temp)
x := or(div(x, exp(2, 40)), mul(temp, exp(2, 160)))
x := or(and(x, 0xFFFFFFFF00FFFFFFFF000000000000FFFFFFFF00FFFFFFFF), mul(or(and(div(x, exp(2, 50)), 0xC0000000), and(div(x, exp(2, 82)), 0x3FFFFFFF)), exp(2, 80)))
}
h := and(add(h, x), 0xFFFFFFFF00FFFFFFFF00FFFFFFFF00FFFFFFFF00FFFFFFFF)
}
h := or(or(or(or(and(div(h, exp(2, 32)), 0xFFFFFFFF00000000000000000000000000000000), and(div(h, exp(2, 24)), 0xFFFFFFFF000000000000000000000000)), and(div(h, exp(2, 16)), 0xFFFFFFFF0000000000000000)), and(div(h, exp(2, 8)), 0xFFFFFFFF00000000)), and(h, 0xFFFFFFFF))
//log1(0, 0, h)
mstore(0, h)
return(12, 20)
}
}
/* Helpers necessary for SHA-1 */
function stringToBytes(string memory source) returns (bytes result) {
assembly {
result := mload(add(source, 32))
}
}
function bytes20ToString(bytes20 x) constant returns (string) {
bytes memory bytesString = new bytes(20);
uint charCount = 0;
for (uint j = 0; j < 20; j++) {
byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (j = 0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
return string(bytesStringTrimmed);
}
}
pragma solidity 0.4.24;
contract pbcoin{
address public owner;
string private lammer;
bytes private gol;
string private seed; //rodl should be the seed. Players will brute it if chall not on blockchain
bytes private seed_in;
/* Only Contract Creator will be the owner */
function pbcoin() public payable {
owner = msg.sender;
}
/* Only owner can set seed, as EVM isn't deployed, we want players to brute it */
function setseed(string _seed) public{
require(msg.sender == owner);
seed = _seed;
seed_in = bytes(seed);
}
function getseed() constant public returns(string){
return seed;
}
/* Encryption Routine */
function encrypt(string text) constant public returns(string) {
uint256 snum = 72;
require(block.number == snum);
uint256 blk_num = snum;
string memory hxhx;
uint256[] memory arr = new uint[](10);
assembly {
mstore(add(arr, 32), 39)
mstore(add(arr, 64), 13)
mstore(add(arr, 96), 93)
mstore(add(arr, 128), 45)
mstore(add(arr, 160), 59)
mstore(add(arr, 192), 68)
mstore(add(arr, 224), 77)
mstore(add(arr, 256), 5)
mstore(add(arr, 288), 2)
mstore(add(arr, 320), 7)
}
// [39,13,93,45,59,68,77,5,2,7]
// string storage rotenc = this.rot7Enc(text);
hxhx = this.rot7Enc(text);
gol = bytes(hxhx);
for (uint i=0; i<gol.length; i++){
gol[i] = (gol[i]^bytes1(arr[(i+5)%10]))^bytes1(block.number); // should be replaced by blk_num
}
require(seed_in.length == 4);
require(this.hmacsha256(bytes(seed), "pbctf{not_the_flag}") == 0x660250a58689ff6ec3acb5efeb01d0ea67599efd2482c1761d5fb81c8b7b5976);
if(gol.length > 6){ // Players will see that real flag should be > 6 obviously
uint offset = gol.length - 2; // Total length - 2
gol[offset-8] = (seed_in[2] ^ gol[offset-8]) ^ seed_in[3] ^ gol[offset-4];
gol[offset-2] = (seed_in[0] ^ gol[offset-2]) ^ seed_in[1] ^ gol[offset-6];
}
else{
assembly{
jump(0x38)
}
}
return string(gol);
}
function fakeFlag() constant public returns(bytes32){
bytes32 _bytes = "pbctf{obviously_not_the_flag}";
return _bytes;
}
function rot7Enc(string text) view public returns(string) {
uint256 length = bytes(text).length;
for (var i = 0; i < length; i++) {
byte char = bytes(text)[i];
//inline assembly to modify the string
assembly {
char := byte(0,char) // get the first byte
if and(gt(char,0x73), lt(char,0x7B))
{ char:= sub(0x60, sub(0x7A,char)) } // subtract from the ascii number a by the difference char is from z.
if iszero(eq(char, 0x20)) // ignore spaces
{mstore8(add(add(text,0x20), mul(i,1)), add(char,7))} // add 7 to char.
}
}
return text;
}
/* SHA-256 Hashing with HMAC */
function hmacsha256(bytes memory key, bytes memory message) public pure returns (bytes32) {
bytes32 keyl;
bytes32 keyr;
uint i;
if (key.length > 64) {
keyl = sha256(key);
} else {
for (i = 0; i < key.length && i < 32; i++)
keyl |= bytes32(uint(key[i]) * 2 ** (8 * (31 - i)));
for (i = 32; i < key.length && i < 64; i++)
keyr |= bytes32(uint(key[i]) * 2 ** (8 * (63 - i)));
}
bytes32 threesix = 0x3636363636363636363636363636363636363636363636363636363636363636;
bytes32 fivec = 0x5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c;
return sha256(fivec ^ keyl, fivec ^ keyr, sha256(threesix ^ keyl, threesix ^ keyr, message));
}
/* Decryption Routine */
function Decrypt(string text) constant public returns(bytes32){
bytes32 _bytes = "Was very bored to implement this";
return _bytes;
}
/* SHA1 useless to increase bytecode size and noise */
function sha1(bytes message) public constant returns(bytes20 ret){
assembly {
switch div(calldataload(0), exp(2, 224))
case 0x1605782b { }
default { revert(0, 0) }
let data := add(calldataload(4), 4)
// Get the data length, and point data at the first byte
let len := calldataload(data)
data := add(data, 32)
// Find the length after padding
let totallen := add(and(add(len, 1), 0xFFFFFFFFFFFFFFC0), 64)
switch lt(sub(totallen, len), 9)
case 1 { totallen := add(totallen, 64) }
let h := 0x6745230100EFCDAB890098BADCFE001032547600C3D2E1F0
for { let i := 0 } lt(i, totallen) { i := add(i, 64) } {
// Load 64 bytes of data
calldatacopy(0, add(data, i), 64)
// If we loaded the last byte, store the terminator byte
switch lt(sub(len, i), 64)
case 1 { mstore8(sub(len, i), 0x80) }
// If this is the last block, store the length
switch eq(i, sub(totallen, 64))
case 1 { mstore(32, or(mload(32), mul(len, 8))) }
// Expand the 16 32-bit words into 80
for { let j := 64 } lt(j, 128) { j := add(j, 12) } {
let temp := xor(xor(mload(sub(j, 12)), mload(sub(j, 32))), xor(mload(sub(j, 56)), mload(sub(j, 64))))
temp := or(and(mul(temp, 2), 0xFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE), and(div(temp, exp(2, 31)), 0x0000000100000001000000010000000100000001000000010000000100000001))
mstore(j, temp)
}
for { let j := 128 } lt(j, 320) { j := add(j, 24) } {
let temp := xor(xor(mload(sub(j, 24)), mload(sub(j, 64))), xor(mload(sub(j, 112)), mload(sub(j, 128))))
temp := or(and(mul(temp, 4), 0xFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFC), and(div(temp, exp(2, 30)), 0x0000000300000003000000030000000300000003000000030000000300000003))
mstore(j, temp)
}
let x := h
let f := 0
let k := 0
for { let j := 0 } lt(j, 80) { j := add(j, 1) } {
switch div(j, 20)
case 0 {
// f = d xor (b and (c xor d))
f := xor(div(x, exp(2, 80)), div(x, exp(2, 40)))
f := and(div(x, exp(2, 120)), f)
f := xor(div(x, exp(2, 40)), f)
k := 0x5A827999
}
case 1{
// f = b xor c xor d
f := xor(div(x, exp(2, 120)), div(x, exp(2, 80)))
f := xor(div(x, exp(2, 40)), f)
k := 0x6ED9EBA1
}
case 2 {
// f = (b and c) or (d and (b or c))
f := or(div(x, exp(2, 120)), div(x, exp(2, 80)))
f := and(div(x, exp(2, 40)), f)
f := or(and(div(x, exp(2, 120)), div(x, exp(2, 80))), f)
k := 0x8F1BBCDC
}
case 3 {
// f = b xor c xor d
f := xor(div(x, exp(2, 120)), div(x, exp(2, 80)))
f := xor(div(x, exp(2, 40)), f)
k := 0xCA62C1D6
}
// temp = (a leftrotate 5) + f + e + k + w[i]
let temp := and(div(x, exp(2, 187)), 0x1F)
temp := or(and(div(x, exp(2, 155)), 0xFFFFFFE0), temp)
temp := add(f, temp)
temp := add(and(x, 0xFFFFFFFF), temp)
temp := add(k, temp)
temp := add(div(mload(mul(j, 4)), exp(2, 224)), temp)
x := or(div(x, exp(2, 40)), mul(temp, exp(2, 160)))
x := or(and(x, 0xFFFFFFFF00FFFFFFFF000000000000FFFFFFFF00FFFFFFFF), mul(or(and(div(x, exp(2, 50)), 0xC0000000), and(div(x, exp(2, 82)), 0x3FFFFFFF)), exp(2, 80)))
}
h := and(add(h, x), 0xFFFFFFFF00FFFFFFFF00FFFFFFFF00FFFFFFFF00FFFFFFFF)
}
h := or(or(or(or(and(div(h, exp(2, 32)), 0xFFFFFFFF00000000000000000000000000000000), and(div(h, exp(2, 24)), 0xFFFFFFFF000000000000000000000000)), and(div(h, exp(2, 16)), 0xFFFFFFFF0000000000000000)), and(div(h, exp(2, 8)), 0xFFFFFFFF00000000)), and(h, 0xFFFFFFFF))
//log1(0, 0, h)
mstore(0, h)
return(12, 20)
}
}
/* Helpers necessary for SHA-1 */
function stringToBytes(string memory source) returns (bytes result) {
assembly {
result := mload(add(source, 32))
}
}
function bytes20ToString(bytes20 x) constant returns (string) {
bytes memory bytesString = new bytes(20);
uint charCount = 0;
for (uint j = 0; j < 20; j++) {
byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (j = 0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
return string(bytesStringTrimmed);
}
}

My intention is that the players use static analysis using existing available decompilers (I won't point out the names), or opcode tools or plugins for well known disassemblers like Ethersplay.

The next step players should look is at the ABI to conclude where to look for functions. There are some filler functions like SHA-1 which increases the byte-code size but is never called during the encrypt function.

PUSH32 strings are important as it gives away information about certain constants. For example: Players can notice HMAC-SHA256 and SHA1 is implemented in the code. The hash is also pushed on the stack. Players can check that the seed size is 4 which is easily brute-forceable locally from 0000-ffff while using that against the hardcoded message to brute the HMAC-SHA256 Hash.

Once, the seed is recovered, players should understand that XOR encryption is used. Players can reverse the commutative property of the XOR (and abuse it) to retrive certain bits and pieces of the ciphertext.

There is an array defined in assembly purely which should be trivial to spot in the byte code and player can see it performs xor on each byte.

Once, Player clears all the XOR specific portion. They will get an ASCII text. Thereafter, they can choose to reverse the ROT-7 function or just guessgod it (as it's trivial and not even require reversing).

Full solve script in solve.py

def decrypt_rotx(ET):
encrypted_text = ET.lower()
number = 7
text = "abcdefghijklmnopqrstuvwxyz"
final = ""
finaldecrypted = ''
rotation = text[number:] + text[:number]
for i in range(len(encrypted_text)):
if encrypted_text[i].isalpha():
final = text[rotation.index(encrypted_text[i])]
finaldecrypted += final
else:
finaldecrypted += encrypted_text[i]
return finaldecrypted
cipher ="76773d393c0227660910577c213e271518791c14".decode('hex')
seed = "rodl" # players should recover it by bruting it against hmacsha256
len_cipher = len(cipher)
offset = len_cipher - 2
cipher = list(cipher)
cipher[offset-8] = chr(ord(seed[2]) ^ ord(cipher[offset-8])^ ord(seed[3]) ^ ord(cipher[offset-4]))
cipher[offset-2] = chr(ord(seed[0]) ^ ord(cipher[offset-2]) ^ ord(seed[1]) ^ ord(cipher[offset-6]))
arr_num = [39,13,93,45,59,68,77,5,2,7] # Must be retrieved carefully from memory array
blk_num = 72
for i in xrange(0, len_cipher):
cipher[i] = chr((ord(cipher[i])^arr_num[(i+5)%10])^(72))
print cipher
print "".join(cipher).encode('hex')
dice = "".join(cipher)
print "pbctf{"+decrypt_rotx(dice)+"}"
# pbctf{skillfulevmremasterz}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment