too young too simple
一個叫flag.bmp的文件,但是無法打開。文件頭42 4D確實是bmp文件的頭,但是文件尾 49 45 4E 44 AE 42 60 82
卻是png文件的尾。
另外文件頭中的IHDR也能確信這是一個png圖片。將文件頭的 42 4D E3 BF 22 00 00 00
修改為png頭 89 50 4E 47 0D 0A 1A 0A
,順利打開得到一張圖片。
圖上是appleu0大神的blog地址,后面的提示意味不明。搜了下weichuncai
并訪問blog才知道這是blog上的動漫人物。與之聊天輸入flag得到Flag。Flag貌似是海賊王里的。大神果然是十足的動漫控啊!
你喜不喜歡萌萌噠的姐姐
一張loli的圖,在jpg尾FF D9后還有很多可顯字符。
全部復制出來,看編碼應該是base64,放到hackbar里base64decode一下,卻得到很多不可顯字符,但是發現了JFIF標識,應該是base64encode了一張圖片得到的。
下面是解碼腳本。
#!/usr/bin/env python
import base64
f = open('1.jpg', 'rb')
pic = f.read()
index = pic.find('\xff\xd9')
flag = pic[index + 5:]
f.close()
f1 = open('flag.jpg', 'w')
f1.write(base64.decodestring(flag))
f1.close()
運行得到flag.jpg。
開胃小菜
題目要求修改參賽口號為Hacked by white god!。
在個人信息頁面http://hack.myclover.org/team_info
的HTML注釋中發現提示:
更新口號翻譯為upvoice,簡直不忍直視,不能再low。 訪問 http://hack.myclover.org/team_info/upvoice?voice=Hacked+by+white+god!
得到Flag。
白神的假期
一張jpg圖片,在文件尾FF D9后還有不少內容,而且是rar文件頭52 61 72 21
。
復制出剩下的部分成rar文件解壓得到flag.txt。
在base64decode一下就得到Flag:KEY:SYC{Y34h!Thi5_15_th3_jp9_r4r_K3Y}
reg
看到com啥的基本上就知道這肯定是個url了,再加上開始部分twi以及com之前的部分是從syclover中取,就能猜出是twitter.com,追加上后面的asdlalalalala得到url:twitter.com/asdlalalalala
,訪問url得到Flag。
bilibili
最坑的題沒有之一。出題者喪心病狂居然要求通過bilibili的會員晉級考試,還得至少80分。好不容易通過修改HTML代碼弄出了一張通過圖,竟然還要關注出題者。無奈只好仔細百度做題,還好這時候只需要60就晉級成功,出題者也無法分辨我到底是60還是80。 ?
HTTP Base1
Flag在HTTP response header中。
HTTP Base2
題目要求必須本機訪問,開始以為加上X-Forwarder-For: 127.0.0.1到request header中就能解決,后來才知道也有從Client-IP來判斷訪問者來路的,于是填上Client-IP: 127.0.0.1到request header中得到Flag。
HTTP Base3
題目顯示訪問者是普通用戶,所以思路是變成管理員,再加上cookie中發現有:userid=33; userlevel=2;于是將userid和userlevel都置為1,再次訪問得到Flag。
CrackPWD1
直接上ophcrack。Ophcrack基于彩虹表來破解hash口令,特別是針對XP的LM-NT hash,成功率很高。 下載地址:
http://sourceforge.jp/projects/ophcrack/releases/
http://sourceforge.net/projects/ophcrack/files/
CrackPWD2
提示口令起始為SYC#
且長度為8,只需要生成一份包含所有可能性的字典交給工具跑。后4位每位上可見字符一共94個,字典大小為94的4次方行,約7800w。
再加上毛子強大的工具oclhashcat(http://hashcat.net/oclhashcat/),幾乎是秒破口令。oclhashcat是一款使用GPU顯卡來破密碼的工具,分為N卡版和A卡版,號稱世界上最快的密碼破解器。 運行命令:
cudaHashcat64.exe -t 32 -m 1000 NT.hash pass.dic
美男子
按提示需要認證為美男子。查看cookie發現是: user=diaosi; isboy=0; pass=d93fa3b25f83f202cc51257eee2c9207;
訪問者被設為diaosi了,不能忍,果斷修改user=meinanzi; isboy=1;
刷新得到Flag。
Cookie中的md5解開是ds0,沒用上。
Login
以username=appleU0&password=syclover
登錄,發現一行提示 Tips: coverage login。 各種搜索不知道啥叫覆蓋登錄。各種亂想終于想到是覆蓋login,變量覆蓋漏洞。經歷ISCC2014的變量覆蓋題,猜變量名是一件頭大的事。我設想了幾個可能的變量名:
admin\flag\key\KEY\user\login\submit
以及可能的值: 1\true\flag\key\admin\flag\login
,爆破了下沒有結果,甚至連中文的值都試過,登錄\提交,無果。最終覺得既然是覆蓋login,變量名應該就是login,于是在GET的url后面添加上?login=1,嘗試了下得到Flag。
白神的shell
直接上代碼吧,多線程也不會,跑的慢點,不過也能出結果。
#!/usr/bin/env python
import httplib
s = 'zxcvbnmasdfghjklqwertyuiop'
length = len(s)
uri = '/pentest/findshell/white_god_s_webshell_'
conn = httplib.HTTPConnection("syc.myclover.org")
for i in range(length):
for j in range(length):
for k in range(length):
conn.request("GET", uri + s[i] + s[j] + s[k] + ".php")
response = conn.getresponse()
response.read()
if response.status == 200:
print "white_god_s_webshell_%s%s%s" % (s[i], s[j], s[k]) + ".php"
exit()
德瑪西亞
下載的dhs文件可以用7z解壓縮,打開解壓的文件發現內容是某用戶訪問baidu的cookie,于是可以用劫持到的cookie冒充該用戶登錄百度。
利用hackbar修改cookie,刷新登錄百度,該用戶的baidu id是dsploit_test。開始以為flag會在網盤、文庫等地方,找了下沒找到,回到個人中心,發現用戶有貼吧操作痕跡,果斷查看發帖和回帖發現Flag。
Web Base1
簡單的Get型注入。
python sqlmap.py –u http://syc.myclover.org/pentest/web1/read.php?id=1 --dbms mysql -D webbase1 -T flag --dump
Web Base2
Post搜索型注入。
python sqlmap.py –u http://syc.myclover.org/pentest/web2/search.php --data “key=my” --dbms mysql -D webbase2 -T #flag --dump
SQL注入
鏈接是sqlmap.org的山寨頁面,在http response header里發現提示,index.php?id=。分別取id=1/2/3/4,頁面與默認頁面均不同。id=4-1與id=3一樣,id=2%2B1與id=3也一樣,id應該就是所需要的注入點了。 如果直接上sqlmap的話,會發現有mysql的payload,但是sqlmap無法識別database類型。
在嘗試多個tamper之后,發現對關鍵字進行保護(對關鍵字添加/!/,如/!select/)的versionedmorekeywords.py能有斬獲,payload發生了變化,也可以跑出一個數據庫。
MySQL的表結構都存放在information_schema中,不能訪問這個庫,就無法知道sqli庫的結構,使用common-tables爆破表名也未果。下圖中無法獲取數據庫的個數,當時覺得可能是過濾了information_schema,也沒有想到好的繞過方法,至此暫時陷入了僵局。
兩天后,主辦方在頁面注釋中給出了新提示,
,
原來是吞掉了payload中的union,select和blank。可以用selselect和uniunionon來bypass。tamper中的nonrecursivereplacement.py剛好提供了此功能,但是需要對其稍作修改,keywords = ("UNION", "SELECT", "INSERT", "UPDATE", "WHERE", "FROM")中的后4項應去掉,只保留UNION和SELECT。這也是我之前使用versionedmorekeywords.py和nonrecursivereplacement.py的組合沒跑出來表的原因。 但是到這一步,能跑出information_schema,也能得出sqli庫的表名i_find_key,卻得不出列名,經比對count(tables)和count(column)的payload,發現count(column)多使用了一個關鍵字AND,于是把AND也加入到nonrecursivereplacement.py的keywords中,最終得到Flag。
PS:最后一步不知列名,也可以靠手注獲得Flag,前提是i_find_key表僅1列。
lfi
既然叫lfi,那就是Local File Inclusion了。
第一步要求從博客訪問lfi頁面,那就加上
referer: http://syclover.sinaapp.com/
根據提示file變量有lfi漏洞,讀下readme.php
既然Flag is in your_heart,那就讀下your_heart.php,得到Flag。
Wireless
生成一個syc19800101-syc20001231的字典,用aircrack-ng跑下就有了。
F4ck
Jsfu*k編碼,復制所有編碼到瀏覽器console處運行,得到Flag。
CodeAudit1
下載附件,對其中index.php進行代碼審查。
<?php
$id = isset($_GET['syc&id']) ? $_GET['syc&id'] : "";
$sql = "SELECT id, title FROM news";
if (!empty($id)) {
$id = mysql_escape_string($id);
$sql .= " WHERE id=$id";
}
//echo $sql; exit;
$result = mysql_query($sql);
$i = 0;
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) :
?>
參數syc&id僅僅使用mysql_escape_string進行了轉義而且還沒有引號保護,這就產生了注入點。我們可以使用union查詢把flag從數據庫中搜出來。從codeaudit1.sql中能獲取數據庫的結構。注意&和#需要編碼為%26和%23。
CodeAudit2
掃描codeaudit2的網頁發現存在首頁備份文件index.php.bak,下載下來看看源碼。
<?php
$username = isset($_POST['username']) ? $_POST['username'] : "";
$password = isset($_POST['password']) ? $_POST['password'] : "";
$type = isset($_COOKIE['type']) ? $_COOKIE['type'] : "1";
if (empty($username) || empty($password) || empty($type)) {
echo "Credits Can not be empty!";
exit;
}
$username = mysql_escape_string($username);
$password = mysql_escape_string($password);
$type = mysql_escape_string($type);
$sql = "SELECT password FROM user WHERE username='${username}' and type=${type}";
$result = mysql_query($sql);
if (mysql_num_rows($result) !== 1) {
echo "System error!";
exit;
}
$row = mysql_fetch_row($result);
if ($row[0] == md5(base64_encode($password))) {
echo "FLAG: *****************";
}
?>
頁面需要正常提供3個參數,username和password是POST型,type是cookie型。我們只需要保證查詢出來的result僅有1行,且輸入的password滿足md5(base64_encode($password))=數據庫中的password,頁面就會自動輸出Flag。
三個參數中username和password有單引號和轉義函數的保護,type參數沒有單引號,因此type是一個cookie型注入點。由于頁面訪問及其不穩定,請求頻率稍微快點服務器就返回502錯誤,而且是基于時間的盲注,即使加上—delay參數,sqlmap也沒能遠程跑出注入點(在本地倒是跑出來基于時間的盲注)。只能尋求手工注入,結合burpsuite,盲注出了username=admin和passoword=fdc4110d6d6612ced3faacd93ee01749。但是password破解不了…,只能另辟蹊徑。
再次審查代碼,發現可以輸入不存在的username,然后利用type的注入點union select任意的密碼,這里可以用concat(16進制的密碼)來bypass對引號的轉義。輸入$password=1,hex(md5(base64_encode($password))) = 0x 6364643936643363633733643164626461666661303363633663643733333962,只需要設置type = 1 union select concat(0x 6364643936643363633733643164626461666661303363633663643733333962)既可得到Flag。
來搞站了
打開鏈接,是myclover.org的一個分站,主辦方還特意提醒不要上“重型掃描器”…
沒啥東西,既然是博客就加上/blog,進了一個wordpress站點。
wordpress通常思路是先找出使用了哪些plugin,然后針對爆出過漏洞的plugin進行滲透。這個站點用wordpress專用掃描器wpscan掃了下,僅有一個插件akismet,是一個過濾垃圾留言的。上www.exploit-db.com搜了下該插件,上次出漏洞已經是7年前,心里頓時哇涼哇涼的。
接著用wpscan枚舉了下用戶名,僅有一個是admin,也順便使用了wpscan和wpbf(http://www.freebuf.com/tools/36904.html)爆破了下admin口令,感覺太慢,沒有結果。雖然后來得知確實是弱口令,而且在弱口令字典中,不知為毛沒有爆出來…
還有個想法就是社工了,本來自己社工就弱,blog的博主名LateRain基本上也是常見詞,毛都沒射出來。
思路陷入停滯狀態,持續了兩天。兩天后,依然沒人得分,我感覺在做的人不多,抱著死馬當活馬醫的想法,于是挑了個時間上了“重型掃描器”——AWVS。果然不負眾望,掃出了弱口令,我心里那個激動啊。
速度使用admin/abc123登進后臺管理。首先上傳插件拿webshell,對shell打了個包,上傳插件,系統提示需要輸入ftp密碼才能上傳,試了下abc123,不對,遂放棄此路,還有其他路子。第二條編輯插件,寫入一句話到頁面中,沒找到保存或更新按鈕,只能換最后一條路了。最后是編輯主題,我在/wp-content/themes/twentytwelve/header.php內插入了一句話,雖然瀏覽器訪問說是500錯誤,但用菜刀還是成功連接。
在站點根目錄下有個fd9c8263b299ee07656aa9e18ac0417a.php
,Flag就在其中。
不知道什么情況主辦方回滾了一次,還把弱口令改了,幸好有前人留下的一句話在 http://pt1.myclover.org/blog/wp-content/themes/twentytwelve/content.php
,密碼是wood。所以能復現成功。 ?
VeryEasy_ELF
既然VeryEasy,直接strings一下,發現疑似flag字符串,拼起來輸入到程序中就是Flag。
如花姐姐
IDA Pro加載一下ruhua.exe,在sub_401410函數中可以看到注冊成功與否的判斷過程。
首先讀取用戶名到v3中,讀取密碼到v5中,用戶名和密碼的長度不能超過10,然后v3和v5分別經函數sub_401500和sub_401530處理后,進行比較,不等就注冊失敗,相等則注冊成功。 再看一下sub_401500和sub_401530。
偽代碼很簡單,用戶名的每一位是(a[i]^3)-20,密碼的每一位則是(a[i]+2)^0x10,據此可以寫出注冊機。
#!/usr/bin/env python
username = raw_input('Username:')
password = ''
for i in range(len(username)):
password += chr((((ord(username[i]) ^ 3) - 20) ^ 0x10) - 2)
print "Password:" + password
syclover對應的密碼就是:JtZIFo@K
BMW
反編譯bmw.apk后,有個TheFlagIsNotHere.java的文件中存在一個getKey()函數,直覺告訴我運行完該函數就能獲得Flag。
新建一個java class,將代碼copy過來,運行得到Flag。Java代碼如下:
public class bmw
{
public static final int LEN = "!0123456789abcdefghijklmnopqrstuvwxyz{}".length();
public static final String SOURCE = "!0123456789abcdefghijklmnopqrstuvwxyz{}";
public static String key = "v}f0frqjudwx4dwl3qv2}3xilqgp71";
public static void main(String[] args)
{
getKey();
}
public static void getKey()
{
StringBuilder stringbuilder = new StringBuilder();
key.length();
int i = 0;
do
{
if(i >= key.length())
return;
int j = "!0123456789abcdefghijklmnopqrstuvwxyz{}".indexOf(key.charAt(i));
if(j == 2)
j = 2 + LEN;
if(j == 1)
j = 1 + LEN;
if(j == 0)
j = LEN;
stringbuilder.append("!0123456789abcdefghijklmnopqrstuvwxyz{}".charAt(j + -3));
i++;
System.out.println(stringbuilder);
} while(true);
}
}
運行結果如圖:
女神
題目給了一個PE程序,首先PEid查了下源程序,帶了UPX的殼,恰好PEid自帶的插件能脫。
脫完之后,OD加載程序,逐步分析,在GetDlgItemTextA處下斷點,從獲取到用戶輸入的Key后開始分析。
004011D4 8D7C24 10 lea edi, dword ptr [esp+0x10] ; esp+0x10=key的起始地址
004011D8 83C9 FF or ecx, 0xFFFFFFFF
004011DB 33C0 xor eax, eax
004011DD F2:AE repne scas byte ptr es:[edi]
004011DF F7D1 not ecx
004011E1 49 dec ecx
004011E2 83F9 0D cmp ecx, 0xD ; length(key)=13
004011E5 0F85 F0000000 jnz 004012DB
004011ED 8A440C 10 mov al, byte ptr [esp+ecx+0x10]
004011F1 3C 30 cmp al, 0x30 ; key[i]>=0x30
004011F3 0F8C E2000000 jl 004012DB
004011F9 3C 39 cmp al, 0x39 ; key[i]<=0x39
004011FB 0F8F DA000000 jg 004012DB
00401207 0FBE7C24 16 movsx edi, byte ptr [esp+0x16] ; edi=key[6]
0040120C 0FBE4C24 10 movsx ecx, byte ptr [esp+0x10] ; ecx=key[0]
00401211 0FBE5424 19 movsx edx, byte ptr [esp+0x19] ; edx=key[9]
00401216 8D4439 A0 lea eax, dword ptr [ecx+edi-0x60] ; eax=key[6]+key[0]-0x60
0040121A 83EA 26 sub edx, 0x26 ; edx=key[9]-0x26
0040121D 3BC2 cmp eax, edx ; key[6]+key[0]-0x60=key[9]-0x26
0040121F 0F85 B6000000 jnz 004012DB
00401225 8A5C24 17 mov bl, byte ptr [esp+0x17] ; bl=key[7]
00401229 8D41 D0 lea eax, dword ptr [ecx-0x30]
0040122C 99 cdq
0040122D 0FBEF3 movsx esi, bl ; esi=key[7]
00401230 83E2 03 and edx, 0x3
00401233 03C2 add eax, edx
00401235 8D56 D0 lea edx, dword ptr [esi-0x30] ; edx=key[7]-0x30
00401238 C1F8 02 sar eax, 0x2 ; eax=(key[0]-0x30)>>2
0040123B 3BC2 cmp eax, edx ; (key[0]-0x30)>>2=key[7]-0x30
0040123D 0F85 98000000 jnz 004012DB
00401243 385C24 14 cmp byte ptr [esp+0x14], bl ; key[4]=key[7]
00401247 0F85 8E000000 jnz 004012DB
0040124D 0FBE4424 11 movsx eax, byte ptr [esp+0x11]
00401252 8D1430 lea edx, dword ptr [eax+esi]
00401255 03D1 add edx, ecx
00401257 03D7 add edx, edi ; edx=key[0]+key[1]+key[6]+key[7]
00401259 81FA D4000000 cmp edx, 0xD4 ; key[0]+key[1]+key[6]+key[7]=0xD4
0040125F 75 7A jnz short 004012DB
00401261 0FBE5424 12 movsx edx, byte ptr [esp+0x12]
00401266 0FBE7424 15 movsx esi, byte ptr [esp+0x15]
0040126B 03F2 add esi, edx ; esi=key[2]+key[5]
0040126D 03C1 add eax, ecx ; eax=key[0]+key[1]
0040126F 3BC6 cmp eax, esi ; key[2]+key[5]=key[0]+key[1]
00401271 75 68 jnz short 004012DB
00401273 0FBE4424 13 movsx eax, byte ptr [esp+0x13] ; eax=key[3]
00401278 42 inc edx ; edx=key[2]+1
00401279 3BD0 cmp edx, eax ; key[2]+1=key[3]
0040127B 75 5E jnz short 004012DB
0040127D 807C24 16 38 cmp byte ptr [esp+0x16], 0x38 ; key[6]=0x38
00401282 75 57 jnz short 004012DB
00401284 807C24 10 39 cmp byte ptr [esp+0x10], 0x39 ; key[0]=0x39
00401289 75 50 jnz short 004012DB
0040128B 807C24 18 30 cmp byte ptr [esp+0x18], 0x30 ; key[8]=0x30
00401290 75 49 jnz short 004012DB
00401294 B1 32 mov cl, 0x32
00401296 384C04 10 cmp byte ptr [esp+eax+0x10], cl ; key[i]=0x32?
0040129A 75 01 jnz short 0040129D
0040129C 45 inc ebp ; ebp=count(key[i]==0x32)
0040129D 40 inc eax
0040129E 83F8 0D cmp eax, 0xD
004012A1 7C F3 jl short 00401296
004012A3 83FD 03 cmp ebp, 0x3 ; count(key[i]==0x32)=3
004012A6 75 33 jnz short 004012DB
004012A8 0FBE4424 1B movsx eax, byte ptr [esp+0x1B]
004012AD 0FBE4C24 1A movsx ecx, byte ptr [esp+0x1A] ; ecx=key[10]
004012B2 8D50 FF lea edx, dword ptr [eax-0x1] ; edx=key[11]-1
004012B5 3BCA cmp ecx, edx ; key[10]=key[11]-1
004012B7 75 22 jnz short 004012DB
004012B9 83C1 D0 add ecx, -0x30
004012BC 83C0 D0 add eax, -0x30
004012BF 0FAFC8 imul ecx, eax ; ecx=key[10]*key[11]
004012C2 0FBE4424 1C movsx eax, byte ptr [esp+0x1C]
004012C7 83E8 30 sub eax, 0x30 ; eax=key[12]
004012CA 33D2 xor edx, edx
004012CC 3BC8 cmp ecx, eax ; key[10]*key[11]=key[12]
有了上述各個條件,加上key2=0x35的提示,容易分析出9156258207236就是Key。
toosimple 附件又是一個apk,反編譯后幾個java文件翻了翻,沒有結果,但是發現了一個libgetKey.so,是個ELF文件,果斷祭出IDA,果不其然發現了關鍵函數calculateKey()。
順著偽代碼寫了個腳本,運行下得到Flag。
#!/usr/bin/env python
str = 'RW@w,!fWj&Bpa=zlemIu6}'
dest = list(str)
v0 = 11
v1 = 1
v2 = 0
while v2 != 21:
v3 = ord(dest[v2])
if v2 > 10:
dest[v2] = chr(v3 - v0)
v0 -= 1
else:
dest[v2] = chr(v3 + v1)
v1 += 1
v2 += 1
print "Flag:"+''.join(dest)
ATM
先本地運行程序,同時用IDA加載。
sub_804859E即輸出上面的部分。要求覆蓋到存放money的內存地址為0x63795324,即$Syc,下圖中對應的是a1的內存地址,同時a1也是sub_804859E函數的參數。
再看下接收輸入的函數sub_804872A。
v22是我們的輸入部分,v24作為存放money的參數帶入sub_804859E中運行。由于沒有對輸入v22的長度做校驗,我們就可以輸入較長字符串來覆蓋掉v24的內存地址,達到目的。下面看下v22和v24之間地址差,以便確定需要多長的shellcode。
因此只需要0x3C-0x1A=34個字符及就能覆蓋掉v22到v24之間的內存地址,再加上$Syc就能使money=0x63795324。因此shellcode可以取為a*34+$Syc。本地溢出的結果如圖。
EasyElf
出了一個VeryEasy_ELF,又來一個EasyELF,IDA加載下,下面的else分支,有疑似flag。輸入的password存在v14處,v14與疑似flag字樣進行比較,比對正確提交卻不正確。真正的True flag在上面的if分支中,與用戶輸入無關。
根據偽代碼的腳本如下,運行得到Flag。
#!/usr/bin/env python
v3 = '\x69\x75\x6f\x63\x67\x71\x70\x67'
v13 = [''] * 8
i = 7
while i >= 0:
v13[i] = chr((ord(v3[7 - i])) - i)
i -= 1
print 'Flag:SYC{'+''.join(v13)+'}'
00xx
開始不知道啥叫SEH,百度百科上是這么說的:SEH("Structured Exception Handling"),即結構化異常處理,是(windows)操作系統提供給程序設計者的強有力的處理程序錯誤或異常的武器。OD加載00xx時,在獲取到用戶輸入后,單步運行很容易產生異常,然后程序就結束了。但是可以通過一些內存地址來跳過這些異常。 首先在GetDlgItemTextW處下斷,輸入后,程序返回到0040111F,運行完0040111F后,應修改EIP(CPU區域右鍵有個New origin here選項就是EIP跳轉功能)直接跳轉到00401136。然后F7進入到目標函數00401190。
運行完00401193,應直接跳轉到004011A3處,從這里開始程序將00403018處的unicode字符串sYC.與00403378處輸入字符串的前4位分別作異或,結果存放在00403388處
運行完004011ED后,應直接跳轉到00401206處,從這里開始程序開始處理輸入字符串第4位之后的部分,0040123A處要求上面異或后的4位相加=wtoi(key[4:])+0x3E,滿足此條件后,再判斷異或后的前3位是不是”C6;”。因此可以退出key的前3位分別是0x43^0x73,0x36^0x59,0x3B^0x43,對應的是”0ox”。
但是這里沒有對key3做限制,事實上只要滿足key4^0x2E+0x76=wtoi(key[4:]),均能注冊成功。
唯一能解釋最終flag(0oxX236)的只有題目叫00xx了。 溢出和SEH這塊確實不怎么會,有不對的地方,請大牛們批評指正。 ?
XOR
題目提示XOR以及與正常程序的比對,那就將out.exe前4字節7D 6B A0 31和正常程序00xx.exe前4字節4D 5A 90 00異或下得到30 31 30 31,猜測所謂的加密就是源程序每兩字節分別與30 31異或,下面是解密代碼。
#!/usr/bin/env python
f1 = open('out.exe', 'r')
out = list(f1.read())
xor = [0x30, 0x31]
for i in range(len(out)):
out[i] = chr(ord(out[i]) ^ xor[i % 2])
f1.close()
f2 = open('xor.exe', 'w')
f2.write(''.join(out))
f2.close()
運行輸出的xor.exe拿到Flag。
LIGHT
開窗游戲的算法,我參考了一篇論文《華容道、開窗等經典智力問題的求解算法研究》。下面是原文中對算法的解釋。 經過分析得知,操作順序不影響操作結果,對一個窗戶操作偶數次等價于操作0次(即不操作);操作奇數次等價于操作1次,故最后的解答的形式可以表示為一個數組a(i, j)。窗子(i, j)不需要操作時,a(i, j)=0;窗子(i, j)需要操作一次時,a(i, j)=1。 因為每個窗戶(i, j)有兩種狀態,一共有mn個,所以一共有2nm 種可能,時間復雜度很大。進一步分析可知,窗子(i, j)的狀態只與以下因素有關:窗子(i, j)的初始狀態,以及a(i, j),a(i-1, j),a(i+1, j),a(i, j-1),a(i, j+1)。假設已經確定第一行的每個窗子是否操作,則對于窗子(1, j),由于窗子(1,j)的初始顏色以及a(1, j),a(i, j-1),a(i, j+1)都已確定,則該窗子的顏色只能由a(i+1, j)來調整。模擬第一行的操作過程后,若a(1, j)仍為開,則必須a(2, j)=1才能使窗子(1, j)滿足條件;同理當a(1, j)為關時,可知a(2, j)=0,這樣a(2, j)(1≤j≤n)也已經確定,依此類推可推出所有a(i, j)的值(1≤i≤m, 1≤j≤n ),最后驗證最后一行窗子是否都已關閉即可。 從而可以枚舉第一行每個窗子是否操作,共需枚舉2n種可能。對于每種可能按以上方法進行遞推,找出其中可使最后一行均為關閉的方案即可。具體算法如下(C#):
/************************************************************
請將1.txt置于Light.exe同路徑下程序自動讀取1.txt的狀態進行求解
************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
namespace LIGHT
{
class Program
{
private static int m = 0, n = 0; // 窗戶的行數m和列數n
private static int[,] window; // 窗戶的狀態數組
private static List<String> solution = new List<string>(); // 最終的解法
static void Main(string[] args)
{
init(); //讀取1.txt并初始化window[m,n]
if (solve())
{
Console.WriteLine("Solved!");
foreach (string s in solution)
{
Console.Write(s + " ");
}
}
else
Console.WriteLine("Can't solve!");
Console.ReadKey();
}
public static void init()
{
List<String> state = new List<string>(); //以行為單位存放1.txt中的內容
try
{
FileStream fs = new FileStream("./1.txt", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string strLine = null;
while ((strLine = sr.ReadLine()) != null)
{
state.Add(strLine);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Environment.Exit(0);
}
m = state.Count;
n = state[0].Length;
window = new int[m, n];
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
window[i, j] = Int32.Parse(state[i][j].ToString());
}
}
}
public static bool solve() // 全變成0,return true; 不能全變成0,return false
{
int max = (int)Math.Pow(2, n);
for (int k = 0; k < max; k++) // 對第一行的所有可能進行枚舉
{
int r = k;
int[,] tmp = new int[m, n];
for (int i = 0; i < m; i++) // 將原始情況復制到臨時數組中操作
{
for (int j = 0; j < n; j++)
{
tmp[i, j] = window[i, j];
}
}
for (int j = 0; j < n; j++) // 第一行
{
if (r % 2 == 1)
{
change(0, j, tmp);
solution.Add("(" + 1 + "," + (j + 1) + ")");
}
r = r / 2;
}
for (int i = 1; i < m; i++) // 遞推后面的行
{
for (int j = 0; j < n; j++)
{
if (tmp[i - 1, j] == 1)
{
change(i, j, tmp);
solution.Add("(" + (i + 1) + "," + (j + 1) + ")");
}
}
}
bool check = true;
for (int j = 0; j < n; j++) // 驗證最后一行是否全是0
{
if (tmp[m - 1, j] == 1)
{
check = false;
solution.Clear();
break;
}
}
if (check)
return true; // 全0有解!
}
return false; // 無解
}
private static void change(int i, int j, int[,] tmp) // 開關窗操作,分9種情形
{
tmp[i, j] = swap(tmp[i, j]);
if (0 < i && i < m - 1 && 0 < j && j < n - 1)
{
tmp[i, j - 1] = swap(tmp[i, j - 1]);
tmp[i - 1, j] = swap(tmp[i - 1, j]);
tmp[i, j + 1] = swap(tmp[i, j + 1]);
tmp[i + 1, j] = swap(tmp[i + 1, j]);
return;
}
if (0 < i && i < m - 1 && j == 0)
{
tmp[i - 1, 0] = swap(tmp[i - 1, 0]);
tmp[i, 1] = swap(tmp[i, 1]);
tmp[i + 1, 0] = swap(tmp[i + 1, 0]);
return;
}
if (i == 0 && 0 < j && j < n - 1)
{
tmp[0, j - 1] = swap(tmp[0, j - 1]);
tmp[0, j + 1] = swap(tmp[0, j + 1]);
tmp[1, j] = swap(tmp[1, j]);
return;
}
if (0 < i && i < m - 1 && j == n - 1)
{
tmp[i, n - 2] = swap(tmp[i, n - 2]);
tmp[i - 1, n - 1] = swap(tmp[i - 1, n - 1]);
tmp[i + 1, n - 1] = swap(tmp[i + 1, n - 1]);
return;
}
if (i == m - 1 && 0 < j && j < n - 1)
{
tmp[m - 1, j - 1] = swap(tmp[m - 1, j - 1]);
tmp[m - 2, j] = swap(tmp[m - 2, j]);
tmp[m - 1, j + 1] = swap(tmp[m - 1, j + 1]);
return;
}
if (i == 0 && j == 0)
{
tmp[0, 1] = swap(tmp[0, 1]);
tmp[1, 0] = swap(tmp[1, 0]);
return;
}
if (i == 0 && j == n - 1)
{
tmp[0, n - 2] = swap(tmp[0, n - 2]);
tmp[1, n - 1] = swap(tmp[1, n - 1]);
return;
}
if (i == m - 1 && j == n - 1)
{
tmp[m - 1, n - 2] = swap(tmp[m - 1, n - 2]);
tmp[m - 2, n - 1] = swap(tmp[m - 2, n - 1]);
return;
}
if (i == m - 1 && j == 0)
{
tmp[m - 2, 0] = swap(tmp[m - 2, 0]);
tmp[m - 1, 1] = swap(tmp[m - 1, 1]);
return;
}
}
private static int swap(int i)
{
return (i == 1) ? 0 : 1;
}
}
}
題中提供初始情況的運行結果如下:
小菜一碟
e有多種取值,但是最終的明文能被識別的很少。鑒于e不大,可以枚舉出所有可能的e,看了下結果,發現有一種情形明文每個位置的值都在130以下,能按ascii解碼。
#!/usr/bin/env python
def foo(num1, num2, cmd):
q = r = s = t = 0
r1 = num1
r2 = num2
s1 = t2 = 1
s2 = t1 = 0
while r2 > 0:
q = int(r1 / r2)
r = r1 % r2
s = s1 - q * s2
t = t1 - q * t2
r1 = r2
r2 = r
s1 = s2
s2 = s
t1 = t2
t2 = t
if cmd == 1: # cmd = 1, return gcd(num1,num2)
return r1
if cmd == 2: # cmd = 2, reyurn num^(-1)(mod num2)
if s1 < 0:
return s1 + num2
return s1
cipher = [1286,7792,11086,13837,4162,11482,3562,383,15995,21350,15374,3562,8713,15995,3267,16051,18518,16194,3562,15995,15374]
n = 23651
f = 23232
length = len(cipher)
e = []
for i in range(3, 10000, 2):
if foo(i, f, 1) == 1:
e.append(i)
for j in range(len(e)):
d = foo(e[j], f, 2)
plain = []
for k in range(length):
plain.append(pow(cipher[k], d, n))
flag = True
for l in range(length):
if plain[l] > 128:
flag = False
if flag:
for m in range(length):
plain[m] = chr(plain[m])
print 'e = %4d d = %5d plain = %s' % (e[j], d, "".join(plain))
運行結果:
?
奇怪的txt
解壓附件得到一個key.txt,中間部分是實際文件的二進制碼,能看出是一個BZ2文件,右側是對應的ASCII值。
寫一個腳本將中間部分復制出來到一個新文件中。
#!/usr/bin/env python
import binascii
f = open('key.txt','r')
content = ''
for line in f.readlines():
content += line[9:49]
f.close()
content = content.replace(' ','')
he = binascii.a2b_hex(content)
f1 = open('newkey.bz2','w')
f1.write(he)
f1.close()
運行后得到newkey.bz2,解壓三次后得到一個key文件。base64decode一下得到FLAG:SYC{L1nux_taR_gZ1p_SYC}。
史上第二難的題目
運行下程序,結果是10000行9位數,多次運行發現每次的結果都一致。將運行結果復制到文件中,讀取文件進行排序。
#!/usr/bin/env python
f = open('lin2.txt','r')
num = list()
for line in f.readlines():
num.append(line[0:9])
f.close()
num.sort()
print num[6249]
運行結果如圖。
lalala
源程序在我的kali上無法運行,一直沒找到libcrypto.so.1.0.0,求大神指教。
不能運行那就上IDA吧,據題意及f5得到的偽代碼來看,這里應該用的是RC4加密算法。輸入的4個ASCII碼數字做密鑰,下圖紅框中應該是密文,用密鑰解密密文,若解出來的明文前三字符以SYC(83 89 67)開頭,則明文就是Flag。
我們就可以針對密文進行爆破,下面是爆破腳本。
#!/usr/bin/env python
def rc4(data, key):
#if the data is a string, convert to hex format.
if(type(data) is type("string")):
tmpData = data
data = []
for tmp in tmpData:
data.append(ord(tmp))
#if the key is a string, convert to hex format.
if(type(key) is type("string")):
tmpKey = key
key = []
for tmp in tmpKey:
key.append(ord(tmp))
#the Key-Scheduling Algorithm
x = 0
box = list(range(256))
for i in range(256):
x = (x + box[i] + key[i % len(key)]) % 256
box[i], box[x] = box[x], box[i]
#the Pseudo-Random Generation Algorithm
x = 0
y = 0
out = []
for c in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
out.append(c ^ box[(box[x] + box[y]) % 256])
result = ""
printable = True
for tmp in out:
if(tmp < 0x21 or tmp > 0x7e):
# there is non-printable character
printable=False
break
result += chr(tmp)
if(printable == False):
result = ""
#convert to hex string
for tmp in out:
result += "{0:02X}".format(tmp)
return result
if __name__ == '__main__':
a = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
b = ')}'
length = len(a)
ciphertext = '\x5F\x20\x6B\x24\x1C\x48\xCA\xFC\xF5\x41\x2D\xD4\xDA'
for i in range(length):
for j in range(length):
for k in range(length):
for l in range(len(b)):
key = a[i] + a[j] + a[k] + b[l]
plaintext = rc4(ciphertext, key)
if plaintext[0:3]=='SYC':
print '%s %s' % (key,plaintext)
exit(0)
其中RC4算法來自http://blog.csdn.net/white_eyes/article/details/6560355
。運行得到
密鑰是@#()。我想了下主辦方干嘛要限制第4個字符呢,于是把第4個字符范圍擴大到所有可見字符,原來還有一解:'g=C,對應的十六進制明文是5359432B17FA53419C25E9979E,恰好也是SYC開頭。
最后附上題目附件及我用到的代碼和參考的論文。部分py如運行不順,請在linux中運行。 鏈接: http://pan.baidu.com/s/1eQs0u6A 密碼: hv8e