Index: wp-admin/admin-ajax.php
===================================================================
--- wp-admin/admin-ajax.php	(revision 22966)
+++ wp-admin/admin-ajax.php	(working copy)
@@ -56,7 +56,7 @@
 	'save-widget', 'set-post-thumbnail', 'date_format', 'time_format', 'wp-fullscreen-save-post',
 	'wp-remove-post-lock', 'dismiss-wp-pointer', 'upload-attachment', 'get-attachment',
 	'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor',
-	'send-attachment-to-editor',
+	'send-attachment-to-editor', 'save-attachment-order',
 );
 
 // Register core Ajax calls.
Index: wp-admin/includes/ajax-actions.php
===================================================================
--- wp-admin/includes/ajax-actions.php	(revision 22966)
+++ wp-admin/includes/ajax-actions.php	(working copy)
@@ -1927,6 +1927,39 @@
 	wp_send_json_success( $attachment );
 }
 
+function wp_ajax_save_attachment_order() {
+	if ( ! isset( $_REQUEST['post_id'] ) )
+		wp_send_json_error();
+
+	if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
+		wp_send_json_error();
+
+	if ( empty( $_REQUEST['attachments'] ) )
+		wp_send_json_error();
+
+	check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
+
+	$attachments = $_REQUEST['attachments'];
+
+	if ( ! current_user_can( 'edit_post', $post_id ) )
+		wp_send_json_error();
+
+	$post = get_post( $post_id, ARRAY_A );
+
+	foreach ( $attachments as $attachment_id => $menu_order ) {
+		if ( ! current_user_can( 'edit_post', $attachment_id ) )
+			continue;
+		if ( ! $attachment = get_post( $attachment_id ) )
+			continue;
+		if ( 'attachment' != $attachment->post_type )
+			continue;
+
+		wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
+	}
+
+	wp_send_json_success();
+}
+
 /**
  * Generates the HTML to send an attachment to the editor.
  * Backwards compatible with the media_send_to_editor filter and the chain
Index: wp-includes/js/media-models.js
===================================================================
--- wp-includes/js/media-models.js	(revision 22966)
+++ wp-includes/js/media-models.js	(working copy)
@@ -523,6 +523,34 @@
 		_requery: function() {
 			if ( this.props.get('query') )
 				this.mirror( Query.get( this.props.toJSON() ) );
+		},
+
+		// If this collection is sorted by `menuOrder`, recalculates and saves
+		// the menu order to the database.
+		saveMenuOrder: function() {
+			if ( 'menuOrder' !== this.props.get('orderby') )
+				return;
+
+			// Removes any uploading attachments, updates each attachment's
+			// menu order, and returns an object with an { id: menuOrder }
+			// mapping to pass to the request.
+			var attachments = this.chain().filter( function( attachment ) {
+				return ! _.isUndefined( attachment.id );
+			}).map( function( attachment, index ) {
+				// Indices start at 1.
+				index = index + 1;
+				attachment.set( 'menuOrder', index );
+				return [ attachment.id, index ];
+			}).object().value();
+
+			if ( _.isEmpty( attachments ) )
+				return;
+
+			return media.post( 'save-attachment-order', {
+				nonce:       media.model.settings.updatePostNonce,
+				post_id:     media.model.settings.postId,
+				attachments: attachments
+			});
 		}
 	}, {
 		comparator: function( a, b, options ) {
Index: wp-includes/js/media-views.js
===================================================================
--- wp-includes/js/media-views.js	(revision 22966)
+++ wp-includes/js/media-views.js	(working copy)
@@ -14,6 +14,7 @@
 
 	// Copy the `postId` setting over to the model settings.
 	media.model.settings.postId = media.view.settings.postId;
+	media.model.settings.updatePostNonce = media.view.settings.nonce.updatePost;
 
 	// Check if the browser supports CSS 3.0 transitions
 	$.support.transition = (function(){
@@ -267,7 +268,8 @@
 			content:    'browse',
 			searchable: true,
 			filterable: false,
-			uploads:    true
+			uploads:    true,
+			sortable:   true
 		},
 
 		initialize: function() {
@@ -2690,7 +2692,6 @@
 			this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
 
 			this.initSortable();
-			this.collection.props.on( 'change:orderby', this.refreshSortable, this );
 
 			_.bindAll( this, 'css' );
 			this.model.on( 'change:edge change:gutter', this.css, this );
@@ -2734,7 +2735,8 @@
 		},
 
 		initSortable: function() {
-			var collection = this.collection,
+			var view = this,
+				collection = this.collection,
 				from;
 
 			if ( ! this.options.sortable || ! $.fn.sortable )
@@ -2760,14 +2762,30 @@
 				// Update the model's index in the collection.
 				// Do so silently, as the view is already accurate.
 				update: function( event, ui ) {
-					var model = collection.at( from );
+					var model = collection.at( from ),
+						comparator = collection.comparator;
 
+					// Temporarily disable the comparator to prevent `add`
+					// from re-sorting.
+					delete collection.comparator;
+
+					// Silently shift the model to its new index.
 					collection.remove( model, {
 						silent: true
 					}).add( model, {
 						at:     ui.item.index(),
 						silent: true
 					});
+
+					// Restore the comparator.
+					collection.comparator = comparator;
+
+					// If the collection is sorted by menu order,
+					// update the menu order.
+					view.saveMenuOrder();
+
+					// Make sure any menu-order-related callbacks are bound.
+					view.refreshSortable();
 				}
 			});
 
@@ -2776,6 +2794,9 @@
 			collection.props.on( 'change:orderby', function() {
 				this.$el.sortable( 'option', 'disabled', !! collection.comparator );
 			}, this );
+
+			this.collection.props.on( 'change:orderby', this.refreshSortable, this );
+			this.refreshSortable();
 		},
 
 		refreshSortable: function() {
@@ -2783,9 +2804,31 @@
 				return;
 
 			// If the `collection` has a `comparator`, disable sorting.
-			this.$el.sortable( 'option', 'disabled', !! this.collection.comparator );
+			var collection = this.collection,
+				orderby = collection.props.get('orderby'),
+				enabled = 'menuOrder' === orderby || ! collection.comparator,
+				hasMenuOrder;
+
+			this.$el.sortable( 'option', 'disabled', ! enabled );
+
+			// Check if any attachments have a specified menu order.
+			hasMenuOrder = this.collection.any( function( attachment ) {
+				return attachment.get('menuOrder');
+			});
+
+			// Always unbind the `saveMenuOrder` callback to prevent multiple
+			// callbacks stacking up.
+			this.collection.off( 'change:uploading', this.saveMenuOrder, this );
+
+			if ( hasMenuOrder )
+				this.collection.on( 'change:uploading', this.saveMenuOrder, this );
+
 		},
 
+		saveMenuOrder: function() {
+			this.collection.saveMenuOrder();
+		},
+
 		createAttachmentView: function( attachment ) {
 			var view = new this.options.AttachmentView({
 				controller: this.controller,
@@ -3049,7 +3092,7 @@
 				}).render() );
 			}
 
-			if ( this.options.sortable ) {
+			if ( this.options.sortable && ! this.options.filters ) {
 				this.toolbar.set( 'dragInfo', new media.View({
 					el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],
 					priority: -40
Index: wp-includes/media.php
===================================================================
--- wp-includes/media.php	(revision 22966)
+++ wp-includes/media.php	(working copy)
@@ -1433,6 +1433,7 @@
 	if ( isset( $args['post'] ) ) {
 		$post = get_post( $args['post'] );
 		$settings['postId'] = $post->ID;
+		$settings['nonce']['updatePost'] = wp_create_nonce( 'update-post_' . $post->ID );
 	}
 
 	$hier = $post && is_post_type_hierarchical( $post->post_type );
