Base64的隐写

0x01 隐写原理

填充两个’=’隐写四位信息,填充一个’=’隐写两位信息,将编码按照base64对照表还原,然后每8位转成字符即可.
base64对照表

0x02 隐写脚本

通过读取源文件source.txt文件把需要隐写的内容隐写进去并保存到stego.txt中。
相应加密脚本enStego.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import base64
import sys
def enStego(sourceFile,setgoFile,message):
b64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
with open(sourceFile,'r') as sourceText, open(setgoFile,'w') as setgoText:
print(message)
message = "".join([bin(ord(i))[2:].zfill(8) for i in message])
for line in sourceText:
text = base64.b64encode(line[:-1].encode("utf-8")).decode("utf-8")
l = text.count('=')
if 0 < 2*l <= len(message):
text = text[:-l-1] + b64table[b64table.index(text[-l-1])+int(message[:2*l],2)] + text[-l:]
message = message[2*l:]
setgoText.write(text+'\n')
if not len(message):
break
return len(message)

if __name__ == "__main__":
if len(sys.argv) == 4:
print("Remaining",enStego(sys.argv[1],sys.argv[2],sys.argv[3]),"bits!")
else:
print("Remaining",enStego("source.txt","stego.txt","SimpleMessage\n"),"bits!")

使用方法:

1
❯ python3 enStego.py source.txt stego.txt 「隐写信息」

source.txt文件内容(任意内容,这里只是放了一段C++代码):

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "linearList.h"
#include <iostream>

template <class T>
struct chainNode
{
T element;
chainNode<T> *next;

chainNode(){}
chainNode(const T& element){
this->element = element;
}
chainNode(const T& element, chainNode<T>* next){
this->element = element;
this->next = next;
}
};

template <class T>
class chain :public linearList<T>
{
public:
chain(int initialCapacity = 10);
chain(const chain<T>&);
~chain();

bool empty() const{
return listSize == 0;
}
int size() const{
return listSize;
}
T& get(int theIndex) const;
int indexOf(const T& theElement) const;
void erase(int theIndex);
void insert(int theIndex, const T& theElement);
void output(std::ostream& out) const;

protected:
void checkIndex(int theIndex) const;
chainNode<T>* firstNode;
int listSize;
};

template <typename T>
chain<T>::chain(int initialCapacity){
if(initialCapacity < 1){
std::ostringstream s;
s << "initial capacity = "<< initialCapacity << " Must be > 0";
throw illegalParameterValue(s.str());
}
firstNode = NULL;
listSize = 0;
}

template <typename T>
chain<T>::chain(const chain<T>& theList){
listSize = theList.listSize;
if (listSize == 0)
{
firstNode = NULL;
return;
}
chainNode<T>* sourceNode = theList.firstNode;
firstNode = new chainNode<T>(sourceNode->element);
sourceNode = sourceNode->next;
chainNode<T>* targetNode = firstNode;
while(sourceNode != NULL){
targetNode->next = new chainNode<T>(sourceNode->element);
targetNode = targetNode->next;
sourceNode = sourceNode->next;
}
targetNode->next = NULL;
}

template <typename T>
chain<T>::~chain(){
while(firstNode != NULL){
chainNode<T>* nextNode = firstNode->next;
delete firstNode;
firstNode = nextNode;
}
}

template <typename T>
T& chain<T>::get(int theIndex) const{
checkIndex(theIndex);
chainNode<T>* currentNode = firstNode;
for(int i = 0; i < theIndex; ++i){
currentNode = currentNode->next;
}
return currentNode->element;
}

template <typename T>
int chain<T>::indexOf(const T& theElement) const{
chainNode<T>* currentNode = firstNode;
int index = 0;
while(currentNode != NULL && currentNode->element != theElement){
currentNode = currentNode->next;
++index;
}
if(currentNode == NULL){
return -1;
}else{
return index;
}
}

template <typename T>
void chain<T>::erase(int theIndex){
checkIndex(theIndex);
chainNode<T>* deleteNode;
if(theIndex == 0){
deleteNode = firstNode;
firstNode = firstNode->next;
}else{
chainNode<T>* p = firstNode;
for(int i = 0;i < theIndex - 1;++i){
p = p->next;
}
deleteNode = p->next;
p->next = p->next->next;
}
--listSize;
delete deleteNode;
}

template <typename T>
void chain<T>::insert(int theIndex, const T& theElement){
if(theIndex < 0 || theIndex > listSize){
std::ostringstream s;
s << "index = " << theIndex << " size = " << listSize;
throw illegalIndex(s.str());
}
if(theIndex == 0){
firstNode = new chainNode<T>(theElement,firstNode);
}else{
chainNode<T>* p = firstNode;
for(int i = 0;i < theIndex - 1;++i){
p = p->next;
}
p->next = new chainNode<T>(theElement,p->next);
}
++listSize;
}

template <typename T>
void chain<T>::output(std::ostringstream& out) const{
for(chainNode<T>* currentNode = firstNode; currentNode != NULL; chainNode = chainNode->next){
out << currentNode->element << " ";
}
}

template <typename T>
std::ostream& operator<<(std::ostream& out, const chain<T>& x){
x.output(out);
return out;
}

int main(int argc, char const *argv[])
{

return 0;
}

得到stego.txt文件内容:

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
I2luY2x1ZGUgImxpbmVhckxpc3QuaCJ=
I2luY2x1ZGUgPGlvc3RyZWFtPp==

dGVtcGxhdGUgPGNsYXNzIFQ+
c3RydWN0IGNoYWluTm9kZZ==
e7==
CVQgZWxlbWVudDs=
CWNoYWluTm9kZTxUPiAqbmV4dDt=

CWNoYWluTm9kZSgpe32=
CWNoYWluTm9kZShjb25zdCBUJiBlbGVtZW50KXs=
CQl0aGlzLT5lbGVtZW50ID0gZWxlbWVudDt=
CX1=
CWNoYWluTm9kZShjb25zdCBUJiBlbGVtZW50LCBjaGFpbk5vZGU8VD4qIG5leHQpe5==
CQl0aGlzLT5lbGVtZW50ID0gZWxlbWVudDv=
CQl0aGlzLT5uZXh0ID0gbmV4dDt=
CX3=
fTu=

dGVtcGxhdGUgPGNsYXNzIFQ+
Y2xhc3MgY2hhaW4gOnB1YmxpYyBsaW5lYXJMaXN0PFQ+
e9==
cHVibGljOo==
CWNoYWluKGludCBpbml0aWFsQ2FwYWNpdHkgPSAxMCk7
CWNoYWluKGNvbnN0IGNoYWluPFQ+Jik7
CX5jaGFpbigpO5==

CWJvb2wgZW1wdHkoKSBjb25zdHu=
CQlyZXR1cm4gbGlzdFNpemUgPT0gMDs=
CX1=
CWludCBzaXplKCkgY29uc3R7
CQlyZXR1cm4gbGlzdFNpemU7
CX1=
CVQmIGdldChpbnQgdGhlSW5kZXgpIGNvbnN0O8==
CWludCBpbmRleE9mKGNvbnN0IFQmIHRoZUVsZW1lbnQpIGNvbnN0O9==
CXZvaWQgZXJhc2UoaW50IHRoZUluZGV4KTu=
CXZvaWQgaW5zZXJ0KGludCB0aGVJbmRleCwgY29uc3QgVCYgdGhlRWxlbWVudCk7
CXZvaWQgb3V0cHV0KHN0ZDo6b3N0cmVhbSYgb3V0KSBjb25zdDt=
CU==
cHJvdGVjdGVkOt==
CXZvaWQgY2hlY2tJbmRleChpbnQgdGhlSW5kZXgpIGNvbnN0O4==
CWNoYWluTm9kZTxUPiogZmlyc3ROb2RlO9==
CWludCBsaXN0U2l6ZTs=
fTt=

dGVtcGxhdGUgPHR5cGVuYW1lIFQ+
Y2hhaW48VD46OmNoYWluKGludCBpbml0aWFsQ2FwYWNpdHkpe3==
CWlmKGluaXRpYWxDYXBhY2l0eSA8IDEpe8==
CQlzdGQ6Om9zdHJpbmdzdHJlYW0gczv=
CQlzIDw8ICJpbml0aWFsIGNhcGFjaXR5ID0gIjw8IGluaXRpYWxDYXBhY2l0eSA8PCAiIE11c3QgYmUgPiAwIjs=
CQl0aHJvdyBpbGxlZ2FsUGFyYW1ldGVyVmFsdWUocy5zdHIoKSk7
CX1=
CWZpcnN0Tm9kZSA9IE5VTEw7
CWxpc3RTaXplID0gMDt=
fc==

dGVtcGxhdGUgPHR5cGVuYW1lIFQ+
Y2hhaW48VD46OmNoYWluKGNvbnN0IGNoYWluPFQ+JiB0aGVMaXN0KXv=
CWxpc3RTaXplID0gdGhlTGlzdC5saXN0U2l6ZTt=
CWlmIChsaXN0U2l6ZSA9PSAwKX==
CXv=
CQlmaXJzdE5vZGUgPSBOVUxMO0==
CQlyZXR1cm47
CX0=
CWNoYWluTm9kZTxUPiogc291cmNlTm9kZSA9IHRoZUxpc3QuZmlyc3ROb2RlO1==
CWZpcnN0Tm9kZSA9IG5ldyBjaGFpbk5vZGU8VD4oc291cmNlTm9kZS0+ZWxlbWVudCk7
CXNvdXJjZU5vZGUgPSBzb3VyY2VOb2RlLT5uZXh0O7==
CWNoYWluTm9kZTxUPiogdGFyZ2V0Tm9kZSA9IGZpcnN0Tm9kZTt=
CXdoaWxlKHNvdXJjZU5vZGUgIT0gTlVMTCl7
CQl0YXJnZXROb2RlLT5uZXh0ID0gbmV3IGNoYWluTm9kZTxUPihzb3VyY2VOb2RlLT5lbGVtZW50KTt=
CQl0YXJnZXROb2RlID0gdGFyZ2V0Tm9kZS0+bmV4dDu=
CQlzb3VyY2VOb2RlID0gc291cmNlTm9kZS0+bmV4dDs=
CX1=
CXRhcmdldE5vZGUtPm5leHQgPSBOVUxMOz==
fS==

dGVtcGxhdGUgPHR5cGVuYW1lIFQ+
Y2hhaW48VD46On5jaGFpbigpe2==
CXdoaWxlKGZpcnN0Tm9kZSAhPSBOVUxMKXu=
CQljaGFpbk5vZGU8VD4qIG5leHROb2RlID0gZmlyc3ROb2RlLT5uZXh0O1==
CQlkZWxldGUgZmlyc3ROb2RlO7==
CQlmaXJzdE5vZGUgPSBuZXh0Tm9kZTu=
CX1=
fZ==

dGVtcGxhdGUgPHR5cGVuYW1lIFQ+
VCYgY2hhaW48VD46OmdldChpbnQgdGhlSW5kZXgpIGNvbnN0e9==
CWNoZWNrSW5kZXgodGhlSW5kZXgpO/==
CWNoYWluTm9kZTxUPiogY3VycmVudE5vZGUgPSBmaXJzdE5vZGU7
CWZvcihpbnQgaSA9IDA7IGkgPCB0aGVJbmRleDsgKytpKXt=

0x03 解密脚本

根据Base64的填充规则,向填充位读出隐藏信息。

相应脚本deStego.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import base64
import sys

def deStego(stegoFile):
b64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
with open(stegoFile,'r') as stegoText:
message = ""
for line in stegoText:
try:
text = line[line.index("=") - 1:-1]
message += "".join([ bin( 0 if i == '=' else b64table.find(i))[2:].zfill(6) for i in text])[2 if text.count('=') ==2 else 4:6]
except:
pass
return "".join([chr(int(message[i:i+8],2)) for i in range(0,len(message),8)])

if __name__ == "__main__":
if len(sys.argv) == 2:
print(deStego(sys.argv[1]))
else:
print(deStego("stego.txt"))

使用方法:

1
2
❯ python3 deStego.py stego.txt
flag{base64_1s_Ama2ing}

得到flag