All the scripts needed to build and extract Linux flags
BIN
resources/flags/linux_flags/10_of_clubs/10 of clubs.png
Normal file
After Width: | Height: | Size: 382 KiB |
BIN
resources/flags/linux_flags/10_of_clubs/10_of_clubs.wav
Normal file
BIN
resources/flags/linux_flags/10_of_clubs/extracted_flag.png
Normal file
After Width: | Height: | Size: 382 KiB |
37
resources/flags/linux_flags/10_of_clubs/get_flag.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: binary -*-
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'zlib'
|
||||
|
||||
def load_wav(fname)
|
||||
File.read(fname)
|
||||
end
|
||||
|
||||
def find_data_chunk_offset(wav)
|
||||
wav.index('data') + 1
|
||||
end
|
||||
|
||||
def get_data_chunk_size(wav)
|
||||
data_chunk_offset = find_data_chunk_offset(wav)
|
||||
wav[data_chunk_offset, 4].unpack('N').first
|
||||
end
|
||||
|
||||
def extract_data_chunk(wav)
|
||||
chunk_offset = find_data_chunk_offset(wav)
|
||||
chunk_size = get_data_chunk_size(wav)
|
||||
|
||||
wav[chunk_offset + 4 + 3, chunk_size]
|
||||
end
|
||||
|
||||
wav_fname = ARGV.shift
|
||||
output = ARGV.shift
|
||||
|
||||
wav = load_wav(wav_fname)
|
||||
data_chunk = extract_data_chunk(wav)
|
||||
data_chunk = Zlib::Inflate.inflate(data_chunk)
|
||||
|
||||
File.open(output, 'wb') do |f|
|
||||
f.write(data_chunk)
|
||||
end
|
||||
|
||||
puts "#{output} written"
|
88
resources/flags/linux_flags/10_of_clubs/make_wave.rb
Normal file
|
@ -0,0 +1,88 @@
|
|||
# -*- coding: binary -*-
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'zlib'
|
||||
|
||||
def load_image(fname)
|
||||
data = File.read(fname)
|
||||
Zlib::Deflate.deflate(data)
|
||||
end
|
||||
|
||||
# http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html
|
||||
def make_wav(data_chunk)
|
||||
# 47202 test size
|
||||
size = 216 + data_chunk.length
|
||||
wav = ''
|
||||
|
||||
# WAV RIFF Header
|
||||
wav << 'RIFF' # groupID
|
||||
wav << [ size ].pack('V') # WAV Size
|
||||
wav << 'WAVE' # Riff Type
|
||||
|
||||
# FORMAT CHUNK
|
||||
wav << 'fmt ' # Chunk ID
|
||||
wav << [ 18 ].pack('V') # Chunk Size
|
||||
wav << [ 0x06 ].pack('v') # Format tag
|
||||
wav << [ 0x02 ].pack('v') # Channels
|
||||
wav << [ 0x1f40].pack('V') # Sample per sec
|
||||
wav << [ 0x3e80 ].pack('V') # Average Bytes per sec
|
||||
wav << [ 0x02 ].pack('v') # Block align
|
||||
wav << [ 0x08 ].pack('v') # Bits per sample
|
||||
wav << [ 0x00 ].pack('v')
|
||||
|
||||
# Fact Chunk
|
||||
wav << 'fact' # Chunk ID
|
||||
wav << [ 0x04 ].pack('V') # Chunk size
|
||||
wav << [ 0x5bc5 ].pack('V') # uncompressed size
|
||||
|
||||
# Data chunk
|
||||
wav << 'data' # Chunk ID
|
||||
wav << [ data_chunk.length ].pack('V') # Chunk size
|
||||
wav << data_chunk
|
||||
|
||||
# afsp Chunk
|
||||
wav << 'afsp' # Chunk ID
|
||||
wav << [ 73 ].pack('V') # Chunk size
|
||||
wav << 'AFspdate: 2003-01-30 03:28:44 UTC'
|
||||
wav << "\x00"
|
||||
wav << 'user: kabal@CAPELLA'
|
||||
wav << "\x00"
|
||||
wav << 'program: CopyAudio'
|
||||
wav << [ 0x00 ].pack('v')
|
||||
|
||||
# List chunk
|
||||
wav << 'LIST' # Chunk ID
|
||||
wav << [ 76 ].pack('V') # Chunk size
|
||||
wav << 'INFO'
|
||||
# Sub chunk: ICRD
|
||||
wav << 'ICRD' # Chunk ID
|
||||
wav << [ 0x18 ].pack('V') # Chunk size
|
||||
wav << '2003-01-30 03:28:44 UTC' # Timestamp
|
||||
wav << "\x00"
|
||||
# Sub Chunk: ISFT
|
||||
wav << 'ISFT' # Chunk ID
|
||||
wav << [ 0x0a ].pack('V') # Chunk Size
|
||||
wav << 'CopyAudio' # Value
|
||||
wav << "\x00"
|
||||
# Sub chunk: ICMT
|
||||
wav << 'ICMT' # Chunk ID
|
||||
wav << [ 0x0e ].pack('V') # Chunk Size
|
||||
wav << 'kabal@CAPELLA' # Value
|
||||
wav << "\x00"
|
||||
|
||||
wav
|
||||
end
|
||||
|
||||
# 'jack_of_clubs.PNG'
|
||||
image_fname = ARGV.shift
|
||||
output = ARGV.shift
|
||||
|
||||
zip_image = load_image(image_fname)
|
||||
wav = make_wav(zip_image)
|
||||
|
||||
File.open(output, 'wb') do |f|
|
||||
f.write(wav)
|
||||
end
|
||||
|
||||
puts "Imaged zipped in a wav file."
|
||||
puts "Wav file written to #{output}"
|
BIN
resources/flags/linux_flags/7_of_diamonds/7_of_diamonds.zip
Normal file
84
resources/flags/linux_flags/7_of_diamonds/create_qr_hint.rb
Normal file
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'rqrcode'
|
||||
require 'fileutils'
|
||||
|
||||
# Installing rmagick is weird.
|
||||
# Do:
|
||||
# brew install imagemagick
|
||||
# brew unlink imagemagick
|
||||
# brew install imagemagick@6 && brew link imagemagick@6 --force
|
||||
# gem install rmagick
|
||||
#
|
||||
# https://stackoverflow.com/questions/39494672/rmagick-installation-cant-find-magickwand-h
|
||||
require 'Rmagick'
|
||||
include Magick
|
||||
|
||||
class SevenOfDiamonds
|
||||
|
||||
TEMPPATH = File.expand_path(File.join(__FILE__, '..', '.temp'))
|
||||
|
||||
def initialize
|
||||
make_temp_folder
|
||||
end
|
||||
|
||||
def make_temp_folder
|
||||
Dir.mkdir(TEMPPATH) unless Dir.exists?(TEMPPATH)
|
||||
end
|
||||
|
||||
def clear
|
||||
FileUtils.rm_rf(TEMPPATH) if Dir.exists?(TEMPPATH)
|
||||
end
|
||||
|
||||
def make_flag(source_image_path, out_path)
|
||||
bin = File.read(source_image_path)
|
||||
h = get_hex(bin)
|
||||
generate_qr_codes(h)
|
||||
make_gif(out_path)
|
||||
end
|
||||
|
||||
def print_status(msg='')
|
||||
puts "[*] #{msg}"
|
||||
end
|
||||
|
||||
def get_hex(bin)
|
||||
h = bin.unpack('H*').first
|
||||
print_status("Hex string size: #{h.length}")
|
||||
h
|
||||
end
|
||||
|
||||
def generate_qr_codes(text)
|
||||
str_length = 50
|
||||
max_fname_length = (text.length / str_length).round.to_s.length
|
||||
counter = 0
|
||||
(0..text.length).step(str_length) do |i|
|
||||
s = text[i, str_length]
|
||||
if !s.nil? && !s.empty?
|
||||
counter += 1
|
||||
qr = RQRCode::QRCode.new(s)
|
||||
File.open(File.join(TEMPPATH, "#{counter.to_s.rjust(max_fname_length, '0')}.png"), 'wb') { |f| f.write(png = qr.as_png) }
|
||||
print_status("QR ##{counter} generated: #{s}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_gif(out_path)
|
||||
gif = ImageList.new(*Dir[".temp/*.png"])
|
||||
gif.delay = 10
|
||||
gif.write(out_path)
|
||||
print_status("GIF written as: #{out_path}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if __FILE__ == $PROGRAM_NAME
|
||||
source_image_path = 'hint.png'
|
||||
out_image_path = 'hint.gif'
|
||||
card = SevenOfDiamonds.new
|
||||
begin
|
||||
card.make_flag(source_image_path, out_image_path)
|
||||
ensure
|
||||
card.clear
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# gem install zxing
|
||||
require 'zxing'
|
||||
require 'fileutils'
|
||||
|
||||
# Installing rmagick is weird.
|
||||
# Do:
|
||||
# brew install imagemagick
|
||||
# brew unlink imagemagick
|
||||
# brew install imagemagick@6 && brew link imagemagick@6 --force
|
||||
# gem install rmagick
|
||||
#
|
||||
# https://stackoverflow.com/questions/39494672/rmagick-installation-cant-find-magickwand-h
|
||||
require 'Rmagick'
|
||||
include Magick
|
||||
|
||||
class CardExtractor
|
||||
|
||||
TEMPPATH = File.expand_path(File.join(__FILE__, '..', '.temp'))
|
||||
|
||||
def initialize(gif_path)
|
||||
make_temp_folder
|
||||
@frames = Image.read(gif_path)
|
||||
end
|
||||
|
||||
def print_status(msg='')
|
||||
puts "[*] #{msg}"
|
||||
end
|
||||
|
||||
def make_temp_folder
|
||||
Dir.mkdir(TEMPPATH) unless Dir.exists?(TEMPPATH)
|
||||
end
|
||||
|
||||
def clear
|
||||
FileUtils.rm_rf(TEMPPATH) if Dir.exists?(TEMPPATH)
|
||||
end
|
||||
|
||||
def extract
|
||||
s = ''
|
||||
print_status("Extracting #{@frames.length} frames...")
|
||||
count = 0
|
||||
@frames.each do |frame|
|
||||
count += 1
|
||||
path = File.join(TEMPPATH, "qr#{count}.png")
|
||||
frame.write(path)
|
||||
qr = convert_qr_to_text(path).strip
|
||||
print_status("Decoded #{File.basename(path)}: #{qr}")
|
||||
s << qr if qr && !qr.empty?
|
||||
end
|
||||
|
||||
File.open('your_zip_hint.png', 'wb') { |f| f.write([s].pack('H*')) }
|
||||
end
|
||||
|
||||
def convert_qr_to_text(path)
|
||||
ZXing.decode(path)
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
gif_path = ARGV.shift
|
||||
if gif_path.nil? || gif_path.empty?
|
||||
puts "[x] Please specify a source GIF file"
|
||||
return
|
||||
end
|
||||
|
||||
ext = CardExtractor.new(gif_path)
|
||||
begin
|
||||
ext.extract
|
||||
ensure
|
||||
ext.clear
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $PROGRAM_NAME
|
||||
main
|
||||
end
|
BIN
resources/flags/linux_flags/7_of_diamonds/hint.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
resources/flags/linux_flags/7_of_diamonds/source.png
Normal file
After Width: | Height: | Size: 452 KiB |
26
resources/flags/linux_flags/7_of_diamonds/zip_png.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# gem install rubyzip
|
||||
require 'zip'
|
||||
|
||||
SOURCEPNG = 'source.png'
|
||||
CARDNAME = '7_of_diamonds.png'
|
||||
ZIP_NAME = '7_of_diamonds.zip'
|
||||
password = ARGV.shift
|
||||
|
||||
if password.nil? || password.empty?
|
||||
puts "[x] Please set a password for the zip file you're trying to create"
|
||||
exit
|
||||
end
|
||||
|
||||
data = File.read(SOURCEPNG)
|
||||
zip = Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new(password)) do |o|
|
||||
o.put_next_entry(CARDNAME)
|
||||
o.write data
|
||||
end
|
||||
|
||||
File.open(ZIP_NAME, 'wb') do |f|
|
||||
f.write(zip.string)
|
||||
end
|
||||
|
||||
puts "[*] #{ZIP_NAME} created with password: #{password}"
|
BIN
resources/flags/linux_flags/8_of_hearts/source.png
Normal file
After Width: | Height: | Size: 403 KiB |
26
resources/flags/linux_flags/8_of_hearts/zip_png.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# gem install rubyzip
|
||||
require 'zip'
|
||||
|
||||
SOURCEPNG = 'source.png'
|
||||
CARDNAME = '8_of_hearts.png'
|
||||
ZIP_NAME = '8_of_hearts.zip'
|
||||
password = ARGV.shift
|
||||
|
||||
if password.nil? || password.empty?
|
||||
puts "[x] Please set a password for the zip file you're trying to create"
|
||||
exit
|
||||
end
|
||||
|
||||
data = File.read(SOURCEPNG)
|
||||
zip = Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new(password)) do |o|
|
||||
o.put_next_entry(CARDNAME)
|
||||
o.write data
|
||||
end
|
||||
|
||||
File.open(ZIP_NAME, 'wb') do |f|
|
||||
f.write(zip.string)
|
||||
end
|
||||
|
||||
puts "[*] #{ZIP_NAME} created with password: #{password}"
|
BIN
resources/flags/linux_flags/ace_of_hearts/chatbot.zip
Normal file
BIN
resources/flags/linux_flags/ace_of_hearts/source.png
Normal file
After Width: | Height: | Size: 458 KiB |
25
resources/flags/linux_flags/joker_flag/convert.rb
Executable file
|
@ -0,0 +1,25 @@
|
|||
require 'chunky_png'
|
||||
|
||||
include ChunkyPNG::Color
|
||||
|
||||
# https://gist.github.com/jeffkreeftmeijer/923084
|
||||
module ChunkyPNG::Color
|
||||
def invert(value)
|
||||
rgb(MAX - r(value), MAX - g(value), MAX - b(value))
|
||||
end
|
||||
end
|
||||
|
||||
source = ARGV.shift
|
||||
dest = ARGV.shift
|
||||
|
||||
# joker-black.png
|
||||
img = ChunkyPNG::Image.from_file(source)
|
||||
img.pixels.map! do |p|
|
||||
if ChunkyPNG::Color.fully_transparent?(p)
|
||||
p
|
||||
else
|
||||
ChunkyPNG::Color.invert(p)
|
||||
end
|
||||
end
|
||||
|
||||
img.save(dest)
|
BIN
resources/flags/linux_flags/joker_flag/joker.png
Normal file
After Width: | Height: | Size: 458 KiB |
1
resources/flags/linux_flags/joker_flag/md5.txt
Normal file
|
@ -0,0 +1 @@
|
|||
5c70e13495405b781e6f231d827a565a
|
BIN
resources/flags/linux_flags/joker_flag/source.png
Normal file
After Width: | Height: | Size: 458 KiB |
BIN
resources/flags/linux_flags/king_of_spades/fake.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
resources/flags/linux_flags/king_of_spades/king_of_spades.png
Normal file
After Width: | Height: | Size: 505 KiB |
6
resources/flags/linux_flags/king_of_spades/make_king_of_spades.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
zip king_of_spades.zip king_of_spades.png
|
||||
cat fake.png king_of_spades.zip > card.png
|
||||
rm king_of_spades.zip
|
||||
echo "Done"
|
18
resources/flags/linux_flags/q_of_hearts/extract_metadata.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'chunky_png'
|
||||
require 'base64'
|
||||
|
||||
img_fname = ARGV.shift
|
||||
|
||||
if img_fname.nil? || img_fname.empty?
|
||||
puts "[*] Please provide a PNG file"
|
||||
exit
|
||||
end
|
||||
|
||||
puts "[*] Extracting Q of Hearts from #{img_fname}..."
|
||||
img = ChunkyPNG::Image.from_file(img_fname)
|
||||
q_of_hearts = Base64::strict_decode64(img.metadata['q_of_hearts'])
|
||||
File.open('real_q_of_hearts.png', 'wb') { |f| f.write(q_of_hearts) }
|
||||
|
||||
puts "[*] Done."
|
BIN
resources/flags/linux_flags/q_of_hearts/fake.png
Normal file
After Width: | Height: | Size: 44 KiB |
17
resources/flags/linux_flags/q_of_hearts/inject_comment.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'chunky_png'
|
||||
require 'base64'
|
||||
|
||||
FAKEPNG = 'fake.png'
|
||||
SOURCEPNG = 'source.png'
|
||||
OUTPNG = 'q_of_hearts.png'
|
||||
|
||||
puts "[*] Injecting Q of Hearts data into #{FAKEPNG}..."
|
||||
source = File.read(SOURCEPNG)
|
||||
b64 = Base64.strict_encode64(source)
|
||||
img = ChunkyPNG::Image.from_file(FAKEPNG)
|
||||
img.metadata['q_of_hearts'] = b64
|
||||
img.save(OUTPNG)
|
||||
|
||||
puts "Done"
|
15
resources/flags/linux_flags/q_of_hearts/inject_metadata.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'chunky_png'
|
||||
require 'base64'
|
||||
|
||||
FAKEPNG = 'fake.png'
|
||||
SOURCEPNG = 'source.png'
|
||||
OUTPNG = 'q_of_hearts.png'
|
||||
|
||||
puts "[*] Injecting Q of Hearts data into #{FAKEPNG}..."
|
||||
source = File.read(SOURCEPNG)
|
||||
b64 = Base64.strict_encode64(source)
|
||||
img = ChunkyPNG::Image.from_file(FAKEPNG)
|
||||
img.metadata['q_of_hearts'] = b64
|
||||
img.save(OUTPNG)
|
BIN
resources/flags/linux_flags/q_of_hearts/q_of_hearts.png
Normal file
After Width: | Height: | Size: 497 KiB |
BIN
resources/flags/linux_flags/q_of_hearts/real_q_of_hearts.png
Normal file
After Width: | Height: | Size: 456 KiB |
BIN
resources/flags/linux_flags/q_of_hearts/source.png
Normal file
After Width: | Height: | Size: 456 KiB |